home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga CD-Sensation: Golden Games
/
Amiga CD-Sensation - Ausgabe 2 - Golden Games (1996)(GTI - Schatztruhe)(DE)[!].iso
/
Specials
/
Inform
/
manuals
/
designers_manual.txt
< prev
Wrap
Text File
|
1995-08-01
|
299KB
|
7,901 lines
This manual describes the `Inform' compiler for adventure games.
Copyright (C) 1994 Graham Nelson
The Inform Designer's Manual
****************************
Last updated 29/9/94
Introduction
************
I will build myself a copper tower
With four ways out and no way in
But mine the glory, mine the power...
-- Louis MacNeice (1907--1963), `Flight of the Heart'
Inform: an adventure game compiler
==================================
Inform is an adventure game compiler, and this is the book to read about
it.
Infocom format `story files' (Adventure games, that is) can be played on
almost any computer, ancient or modern, and interpreters which run them
are widely available, from personal organisers to mainframes. They
represent probably the most portable form in which games can ever be
written, as no alteration whatever is required to move a game from one
model of computer to another.
Inform is not just a compiler but an `operating system' too: its library
(a suite of standard game routines) allows designers to begin coding at
once. An Inform source file need not contain any of the parser code, or
the running of the `game universe', only descriptions and exceptions to
the usual rules. This world is quite rich already, having over 80 verbs
and an extensive grammar: the library understands rooms, objects,
duplicates, containers, doors, things on top of other things, light,
scoring, switching things on and off, opening, closing and locking
things, entering things, travelling about in them and so forth. The
parser it uses (which can be entirely invisible to the designer, but is
programmable and very flexible) is sophisticated enough to handle
ambiguities, clarify its input by asking questions and to cope properly
with plurals, vagueness, conversation, pronouns and the player becoming
someone else in mid-game.
This manual makes occasional reference to the example games provided
with Inform, at present `Advent' (a full version of the original
mainframe Adventure, which contains a good deal of `everyday Inform'),
`Toyshop' (a collection of unusual objects to play with) and `Balances'
(a short story consisting of puzzles making great use of the parser's
flexibility). Try to get hold of these programs and preferably print
them out. There is also a `Shell' game consisting of the minimum code
to get going, but all 14 lines of it are given anyway (*note
Starting::.).
History of the Designer's Manual
================================
The text of this book has evolved from five earlier editions. The old
manual was in places rather technical, with a makeshift and sometimes
defensive tone (`Inform is an easel, not a painting'). There were
specifications of the run-time code format and literary critiques of
games gone by: like an oven manual padded out with both a cookery book
and a detailed plan of the gas mains. This book contains just the
instructions for the oven.
So there are three `companion volumes'. `The Craft of Adventure' is an
essay on the design of adventure games; `The Specification of the
Z-Machine' covers the run-time format and Inform assembly language, its
lowest level; and `The Inform Technical Manual' documents chiefly
internals, for compiler maintenance and porting.
In trying to be both a tutorial and reference work in a budget of about
100 pages of A4, this book aims itself in style halfway between the two
extremes of manual, Tedium and Gnawfinger's `Elements of Batch
Processing in COBOL-66', third edition, and Mr Blobby's `Blobby Book of
Computer Fun'. (This makes some sections both leaden *and*
patronising.) I have tried to make every passage tell the truth, so
that even early sections are reliable for reference purposes. Passages
which divert the main story, usually to tell the unexpurgated truth when
this may just confuse the newcomer, are marked with warning exclamation
marks, thus: `!!!!'. Lengthy examples are left as exercises, with
answers in the Appendix; which also contains a language specification,
some reference lists, definitions of attributes and properties, and a
large index.
The manual was converted to Texinfo format by Gareth Rees.
Copyright and copying permissions
=================================
The copyright on Inform, the program and its source code, its example
games and documentation (including this book) is retained by Graham
Nelson, who asserts the moral right to be identified as its author.
Having said this, I am happy for it to be freely distributed to anybody
who wants a copy, provided that: (a) distributed copies are not
substantially different from those archived by the author, (b) this and
other copyright messages are always retained in full, and (c) no profit
is involved. However, a story file produced with the Inform compiler
(and libraries) then belongs to its author, and may be sold for profit
if desired, provided that its game banner contains the information that
it was compiled by Inform, and the Inform version number.
At present, the best source for Inform material (executables of the
compiler for different machines, source code, the library files and
example games) is the anonymous ftp site `ftp.gmd.de', and its home
directory is:
/if-archive/infocom/compilers/inform
Credits and dedication
======================
Some of the ideas of Inform came from an incremental multi-player game
called Tera, on the Cambridge University mainframe, written by Dilip
Sequeira and the author in 1990 (whose compiler was called Teraform); in
turn, this stole a little from David Seal and Jonathan Thackray's game
assembler; which dates back to the late 1970s and was written for
`Acheton', perhaps the first worthwhile game written outside America.
Still, much of the Inform kernel derives ultimately from the `IEEE
Computer' article `Zork: A Computerized Fantasy Simulation Game' by P.
David Lebling, Marc S. Blank and Timothy A. Anderson; and more was
suggested by Richard Tucker, among others.
With the advent of Inform 5 in mid-1994, I feel that the underlying
language came to a satisfactory state. The library files, however, may
well go on being improved, since users can update these much more easily
than the compiler (changes to which mean work for many people). The
present library files (release 5/4) are greatly enhanced from the Inform
5.2 files, but compatible with them (except in handling plural nouns and
that the obselete attribute `autosearch' has been withdrawn).
The list of those who have helped the project along is legion: I should
like to thank them all, porters, users and critics alike, but especially
Volker Blasius, Paul David Doherty, Mark Howell, Bob Newell, Robert
Pelak, Gareth Rees, Jørund Rian, Dilip Sequeira, Richard Tucker and
Christopher Wichura.
One final word. I should like to dedicate this book, impertinently
perhaps, to our illustrious predecessors: Willie Crowther, Don Woods and
the authors of Infocom, Inc.
Graham Nelson
Magdalen College, Oxford
September 1994
1 Getting started
*****************
1.1 The `Shell' game
====================
The very first thing to try is to compile the `Hello Cruel World' game,
a very short test file supplied with Inform. If that compiles and runs
properly (producing a short page of text, then finishing), try the
following:
Constant Story "SHELL";
Constant Headline "^An Interactive Skeleton^\
Copyright (c) 1994 by (your name here).^";
Include "Parser";
Include "VerbLib";
Object Blank_Room "Blank Room"
with description "An empty room."
has light;
[ Initialise;
location=Blank_Room;
"^^^^^Welcome to the shell...^^";
];
Include "Grammar";
end;
If this compiles, Inform is probably set up and working properly. It
takes a short while to compile, because it `includes' three large
standard files, containing a large amount of code. (`Include' lines
are also sometimes written `#include' to make C programmers feel more
at home.) The library files are:
`Parser'
The core of the game, and a full parser
`Verblib'
Routines for many game verbs, like `take'
`Grammar'
A grammar table to decode the player's input from
Together, they make up the `library'. They can certainly be modified by
designers, but great effort has gone into making sure the need hardly
ever arises.
Apart from that, the code contains:
1. strings giving the name of the game, and a copyright message, to be
printed out at the appropriate moments;
2. a routine, called `Initialise', which is run when the game begins,
and simply sets where the player starts (in the obvious place!) and
prints a welcoming message;
3. an object, to be the only room of the game.
The `Shell' game is very boring: there is nothing for the player to do
but wait and quit.
1.2 Making simple objects
=========================
In Inform, everything is an object: rooms and things to be picked up,
scenery, intangible things like mist and even some abstract ideas (like
the direction `north'). More about this later: for now, here's a new
object, to go under the `Blank_Room' definition:
Nearby cone "green cone"
with name "green" "cone";
(`Nearby' just means it's an object inside the last thing declared as
an `Object', in this case the Blank Room.) A green cone now appears in
the Blank Room. The player can call it either `green cone', `cone' or
even `green'. It can be taken, dropped, looked at, looked under and so
on.
This is still rather plain. Examining the cone sees `nothing special
about the green cone", for instance. So we might extend the definition
by:
Nearby cone "green cone"
with name "green" "cone" "emerald",
initial "Nearby is an emerald green cone, one foot high.";
The `initial' message now appears when we arrive in the Empty Room.
Taking things a little further...
Nearby cone "green cone"
with name "green" "cone" "emerald" "marzipan",
initial "Nearby is an emerald green cone, one foot high.",
description "The cone seems to be made of emerald-coloured \
marzipan."
has edible;
(Note that the description is split across two lines: the `\' makes the
message come out as one sentence without a huge space.) Now if we
examine the cone, we get its surprising `description': and the player
is allowed to eat the cone.
!!!! `name', `description' and `initial' are examples of `properties',
while `edible' is an `attribute': the difference is that the former
have values, whereas the latter are just on or off.
So far, we're just filling in a form, and we could go much further doing
this, but that wouldn't be much of an example. Instead, some honest
programming:
Nearby cone "green cone"
with name "green" "cone" "emerald" "marzipan",
initial "Nearby is an emerald green cone, one foot high.",
description "The cone seems to be made of emerald-coloured \
marzipan.",
after [;
Take: "Taken. (Your hands are smeared with marzipan.)";
Drop: "The cone drops to the floor and sags a little.";
],
has edible;
The property `after' doesn't just have a string for a value: it has a
routine of its own. Now what happens is that when an action happens to
the cone, the `after' routine is called to apply any special rules
about the cone. In this case, `Take' and `Drop' are the only actions
tampered with: and the only effect is that the usual messages (`Taken.'
`Dropped.') are replaced.
!!!! There's no real difference between routines like `Initialise',
which are defined outside objects at the `top level', and routines
inside objects like this `after' routine: except that outside routines
have to have names, since they aren't identified by belonging to any
particular object.
1.3 Adding code to objects
==========================
Still, the cone doesn't actually do anything! So here it is with a
(completely unfair) puzzle added:
Nearby cone "green cone"
with name "green" "cone" "emerald" "marzipan",
initial "Nearby is an emerald green cone, one foot high.",
description "The cone seems to be made of emerald-coloured \
marzipan.",
before [;
Eat:
if (random(100) <= 30) {
deadflag = 1;
"Unfortunately, you seem to be allergic to almonds.";
}
"You nibble at a corner of the cone.";
],
after [;
Take: "Taken. (Your hands are smeared with marzipan.)";
Drop: "The cone drops to the floor and sags a little.";
],
has edible;
The `before' routine is called before the player's intended action
takes place. So when the player tries typing, say, `eat the cone',
what happens is: in 30% of cases, she dies of almond poisoning; and in
the other 70%, she simply nibbles a corner of the cone (without actually
consuming it completely).
!!!! Like many programming languages, Inform braces together blocks of
code. `deadflag' is a global variable, whose value does not belong to
any particular object (or routine). It is defined somewhere in the
depths of the library: it's usually 0; setting it to 1 causes the game
to be lost, and setting it to 2 causes a win.
In either case, the usual rule for the `Eat' action is never applied.
This is because, although it isn't obvious from the code, the routine
actually returns a value, true or false. And the command
"Unfortunately, you seem to be allergic to almonds.";
not only prints the message (together with a carriage return), but also
returns true from the `before' routine. Since the routine normally
returns false, the library knows that something has happened to
interrupt the usual rules of the game.
1.3.1 Exercise: the green cone
------------------------------
Extend the green cone so that it is described as sagging after it has
been dropped back on the ground. (You need a few more properties for
this.)
*Note Answer (the green cone)::.
2 Ingredients and lexicon
*************************
Properly speaking, `Inform' means the compiler and its language: whereas
this book is chiefly about the `library' of code which comes with it.
For most practical purposes this makes no difference - but it's worth
remembering that in the last resort you can change almost anything about
how the library works.
2.1 The basic ingredients of a game
===================================
The basic ingredients of an Inform game are:
*Objects*, *Routines*, *Actions* and *Grammar*.
We have already seen examples of Objects and Routines. Actions happen
in the course of play. The main loop of a game looks like this:
1. Ask the player to type something
2. Parse this (i.e., decide what it means) and generate any actions it
calls for
3. Work out the consequences of these actions and tell the player
4. Worry about time passing by, and other things happening
a process which ends only in victory or death. (In this respect it is
unlike life.)
Probably the most complicated programming in any adventure game goes
into the parser. Inform's parser is fairly advanced, and is designed
with the intention of being highly programmable at all levels to suit
your game: so this manual will be returning to features of the parser
again and again. It isn't perfect by any means, but it does understand
things like:
throw three of the coins into the fountain
write funny on the lighted cube
take the sword and all the crowns
what is a grue
dwarf, give me the battleaxe
It also asks questions when it needs to, makes inferences from partial
requests and tries to make good guesses when faced with ambiguous
requests. You can teach it new verbs and new forms of old ones: this is
Grammar. (The library starts with about 85 verbs, not counting
synonyms.)
Until you need to start programming it, the parser sits in the
background, and you need to know nothing about it. But the parser is
where Actions come from. An Action can have up to two objects attached
to it, and represents something which the player is trying to do. For
instance,
Inv Take sword Insert gold_coin cloth_bag
are all actions. (By this stage the game has reduced them to three
numbers each, the action number and two object numbers: there's no text
floating about.)
!!!! Actions are also sometimes produced as a knock-on effect by other
actions, and they can also be produced by your own code: you can even
create new actions altogether, which are treated no differently from the
standard ones.
2.1.1 Exercise: actions
-----------------------
Compile one of the supplied example games, say `Toyshop', with the
`DEBUG' option defined (*note Debugging::.) so that you can use the
special `actions' verb to see exactly what all the actions are as they
happen.
*Note Answer (actions)::.
2.2 Introduction to the Z-machine
=================================
!!!! Inform can produce two kinds of game: `Standard' and `Advanced'.
Code is in almost every case portable between these two forms, so that
you can easily make a version each way from the same program. The
latter is better, but just in case you have a small computer (such as a
personal organiser) or a very bad run-time interpreter, you might
prefer the former. Occasionally this manual will mention a difference
between these forms.
!!!!! At this point, we should describe the imaginary machine, sometimes
called the Z-machine (Z is for `Zork') which Inform compiles games for.
An `interpreter' is a program which simulates this imaginary machine,
so that it can run the game files that Inform produces: the same files
will work on any interpreter on any machine. Interpreters are
available for very many machines indeed. The Z-machine is, except in a
few corners, a beautiful design and is described in great detail in the
`Specification of the Z-Machine'. For now, a few words about the
memory map. The memory of this imaginary computer is 128K long for
Standard games and 256K long for advanced ones: the format is extremely
well compressed, so this is actually quite a large memory.
!!!!! Numbers are stored in 16-bit words (2 bytes long), and are signed
in the usual way, so they hold values -32768 <= n <= 32767 with the
hexadecimal value `$ffff' being the same as -1. The sign doesn't mean
anything for addresses, of course, but there's a catch: given that a
2-byte number can only hold a value 0 <= n <= 65535, how can an address
in a 128K or 256K memory map be held? The answer is that there are two
kinds of address: a `packed address' and a `byte address'. A byte
address is just an address in the bottom 64K of memory, and the
Z-machine is arranged so that everything which could ever change is
there: so these are the only addresses which matter very much. Packed
addresses are addresses divided by 2 (or by 4 in Advanced games), so
they can only refer to even (divisible by four) locations in memory.
They hold the addresses of long strings and routines, which Inform is
careful always to put at even (divisible by four) locations.
3 Objects, properties and attributes
************************************
Objects make up the substance of the world. That is why they
cannot be composite.
- Ludwig Wittgenstein (1889-1951), `Tractatus'
...making philosophical sense of change runs up against what seem
to be impossible philosophical difficulties. Aristotle...
focuses on the central case of an object coming to have a property
that it formerly lacked.
- Julia Annas, `Classical Greek Philosophy'
3.1 The tree of objects
=======================
The objects of the game form what is sometimes called a `tree', though a
better analogy would be a forest, and anyway one usually draws the whole
thing upside down and uses the language of `families' - calling them
`children' and `parents' of each other. Anyway, here's an example:
Meadow
|
Mailbox -> Player
| |
Note Sceptre -> Cucumber -> Torch -> Magic Rod
|
Battery
This is a family tree, then: each object has a parent, a sibling and a
child, though that object may be the special `nothing' object. (The
`Cucumber' has child `nothing', for instance.)
As the game goes on, objects move around: when an object moves, all its
possessions (that is, children) go with it. The Inform command to move
an object is
move object to new-owner;
and it must be emphasized that this prints nothing on the screen, and
indeed does nothing except to change the tree above.
Inform provides special functions for reading this tree.
`parent', `sibling' and `child'
all do the obvious things, and in addition there's a function called
`children' which counts up how many children an object has (only
children: grandchildren aren't counted). For instance,
parent ( Mailbox ) = Meadow
children ( Player ) = 4
child ( Sceptre ) = 0
sibling ( Torch ) = Magic Rod
!!!! `nothing' isn't really an object: it's just a convenient name for
the number 0, which is the object number meaning `no such object'. It
isn't a good idea to meddle with `nothing', or to apply functions like
`parent' to it, but then there's never any need.
!!!!! When an object is added to the possessions held by another, it
appears at the end of the list. Thus, the eldest child is first in the
list and the youngest is last. Inform also provides functions
`youngest'
end of list of possessions
`eldest'
same as `child'
`younger'
same as `sibling', i.e., rightwards in the picture
`elder'
reverse of `sibling', i.e., leftwards in the picture
3.2 Attributes
==============
Objects have more to them than just where they are in the tree. They
also have collections of variables attached to them.
Firstly, there are flags, called `attributes', which can be either set
or clear. These might be such conditions as `giving light', `currently
worn' or `is one of the featureless white cubes'. Attributes all have
names, which are a single word: like `light', for instance, which
indicates that something is giving off light. There are about 30
defined by the library. They can be tested with a condition like
if (obj has locked) "But it's locked!";
and can be set with the `give' command. So, for instance,
give brass_lantern light;
give iron_door locked;
and they can be taken away with
give brass_lantern ~light;
give fake_coin ~scored;
the `~' sign (or tilde) standing for negation. In fact you can give or
take many at one go, so for instance
give wooden_door open openable ~locked;
!!!! You can make your own attributes with a directive like
Attribute offered_to_ogre;
at the start of the program. (A `directive' is a command to Inform
directly, and happens at compile-time: not something in a routine which
is to happen in the course of play.)
!!!!! In Standard games there are few spare attributes available because
the library takes most of them. To get around this limit there's a
convenient dodge. It sometimes happens that an attribute is only
meaningful for a particular kind of object: for instance, `spell has
been read' might only be meaningful for a `scroll'. With care,
therefore, one may re-use the same attribute to have different meanings
for different kinds of object. The syntax to declare that an attribute
is being reused is
Attribute <new> alias <old>;
Thereafter Inform will treat the new and old attribute names as
referring to the same attribute: it's up to the programmer to make sure
this does not lead to inconsistencies. (The library already indulges in
a certain amount of this chicanery.)
3.3 Properties
==============
Secondly, there are `properties'. These are far more elaborate, and not
every object has every property. For instance: not every object has the
`door_to' property (it holds the place a door leads to, so things other
than doors don't usually have it). The current value of a property is
got at by constructions like:
iron_door.door_to
crystal_bridge.door_to
green_cone.before
diamond.initial
You can read the value of `door_to' for something like the `diamond',
and you'll just get a dull value like `nothing', but you can't write to
it: that is, you can't change `diamond.door_to' unless you declared a
`door_to' property for it.
!!!! You can also define your own properties (again, subject to
availability, because the library claims many of them).
Property door_to;
Property article "a";
Property blorple_routine $ffff;
are all examples of the `Property' directive. In the case of
`article', we are saying that the value `"a"' should be the default
value for any object which doesn't declare an `article'.
!!!!! Properties can also `alias'. They can also be declared as `long'
or `additive', which is a complicated matter: basically, we'll come to
`additive' when discussing classes, and properties which might start as
small numbers (less than 256) and be changed into large ones in play,
ought to be declared as `long'.
As will be seen from examples, a property value can be many things: a
string like `"frog"', a number such as `$ffff' (this is the Inform way
of writing numbers in hexadecimal), an object or a routine. You can
change the current value by something like
location.door_to = hall_of_mists;
brass_lantern.short_name = "dimly lit brass lantern";
grenade.time_left = 45;
*WARNING:* The game may crash at run-time if you attempt to write to a
property field which an object hasn't got. So although you can read an
undeclared property (you just get the default value), you can't write to
one. (Also, you can't extend a property beyond its length: see below.)
!!!! The Inform language does not have types as such, and strings and
routines are stored as numbers: as their addresses inside the virtual
machine, in fact. This means that Inform thinks
"Hello there" + 45 + 'a'
is a perfectly sensible calculation. It's up to you to be careful.
!!!!! A property can hold more than just one number (be it interpreted
as a string, a routine or whatever): it can hold a small array of
numbers. In Standard games it can have four numbers (8 bytes' worth),
and in Advanced games 32 (64 bytes). For instance, an entry in an
object definition might read
found_in Marble_Hall Arched_Passage Stone_Stairs,
storing a sequence of three values (all objects) in the `found_in'
property. To read or write to this array, you need to know its (byte)
address and its length. The operators
`object.&property' and `object.#property'
tell you these. (Be warned: `object.#property' tells you the number of
bytes, not the number of words.) If you give a property more than 8
bytes of data in a Standard game, Inform warns you and takes only the
first 8, but does not cause an error: this is so that, say,
Object ...
with name "radio" "wireless" "transistor" "portable" "stereo" "tranny",
...
will compile either way (but the last two synonyms for `"radio"' will
not enter the dictionary if it's being compiled in Standard form, since
a name takes two bytes).
3.3.1 Exercise: property addresses
----------------------------------
!!!!! Use the `object.&property' construction to find out whether the
object in variable `obj' has the `door_to' property defined or not.
*Note Answer (property addresses)::.
3.4 Making object definitions
=============================
Time to make some object definitions. A typical object definition looks
something like:
Object trapdoor "hinged trapdoor" attic
with name "hinged" "trap" "door" "trapdoor",
when_open "A hinged trapdoor in the floor stands open, and \
light streams in from below.",
when_closed "There is a closed trapdoor in the middle of the \
floor.",
door_to house,
door_dir d_to,
has door static open light openable;
This is the conventional way to lay out an `Object' declaration: with
the header first, then `with' a list of properties and their starting
values, finishing up with the attributes it initially `has'.
`trapdoor' is the name given to the object in the program, and it
becomes a constant from then on (whose value is the number of the
object). The `attic' is the object which the `trapdoor' starts out
belonging to (as any player of `Curses' will know). Some objects start
out not belonging to anything (rooms, for example, or treasures which
only appear half-way through). You can declare these as belonging to
`nothing', but it's better just to miss this out altogether:
Object trapdoor "hinged trapdoor"
with ...
If you do declare an object already belonging to another, as above, then
the other object must already have been defined. (This is no real
restriction, and ensures that you can't set up a `loop' - one in another
in a third in the first, for instance.)
Objects can also be declared, in an identical way, by the `Nearby'
directive. The only difference is that no initial-owner object can be
given; it starts out belonging to the last thing declared as an
`Object'. For example, in
Object hillside "Panoramic Hillside"
with ...
Nearby scenery "scenery"
with ...
the `hillside' is a room to which the `scenery' will belong.
Otherwise, `Nearby' is the same as `Object', and this is just a
convenience to make it easier to move things around in Inform code by
cutting definitions out and pasting them in elsewhere.
Some properties of objects should be routines. For instance, an object
can have a `describe' property which is a routine to print out a
description of it.
These could just be listed as names of routines, but usually the actual
routine is written out then and there as the property value. For
instance, in the classic Adventure object
Nearby tasty_food "tasty food"
with description "Sure looks yummy!",
initial "There is tasty food here.",
name "food" "ration" "rations" "tripe" "yummy" "tasty"
"delicious" "scrumptious",
after [;
Eat: "Delicious!";
],
article "some"
has edible;
the `after' property does not name a routine but instead defines it.
No name is needed or given for the routine. (The semicolon after the
`[' is needed to show that the routine has no local variables.)
The routine must end with either `],' or `];'. If `],' the object
definition can resume where it left off, with further properties. (If
it ends with `];', then the object definition ends where the routine
finishes.)
!!!! The rules for embedded routines are not quite the same as those for
ordinary routines. By default, embedded routines return `false', or 0
(instead of `true', or 1, which other routines return by default).
Also, the handy shorthand:
Action [, Action2 ...] : ...some code...
is provided, which executes the code only if the action being considered
is the one named.
!!!!! It actually does this by setting a special variable called
`sw__var' to the action number: when it compiles `Action1:' it just
compiles an `if' statement which sees whether `sw__var' has value
`Action1'.
!!!! Properly speaking, the full syntax of the header is
`Object' OBJ-NAME-1 ... OBJ-NAME-N `"short name"' [PARENT-OBJ]
*or* `Nearby' OBJ-NAME-1 ... OBJ-NAME-N `"short name"'
*or* `Class' CLASS-NAME
and the parent object must have already been defined. A `Class'
definition is very like an object definition, and will be discussed
later on. The syntax for an object, then, is
HEADER [`,']
`class' CLASS-1 ... CLASS-N [`,']
`with' PROPERTY-NAME-1 VALUE-1 ... VALUE-N`,'
PROPERTY-NAME-2 VALUE-1 ... VALUE-N`,'
...
PROPERTY-NAME-N VALUE-1 ... VALUE-N [`,']
`has' ATT-1 ... ATT-N
Although it's conventional to write `class', `with' and `has' in this
order, actually they can be in any order and any or all can be omitted
altogether: and the commas in square brackets `[,]' are optional in
between these fields. (The classes listed under `class' are those
which the object inherits from.)
!!!! One property is treated differently from the others, and this is
the special property `name'. Its data must be a list of English words
in double-quotes, as in all the above examples. (Probably the most
annoying restriction of Standard games is that this means only 4 names
at most can be accommodated in that format: you get up to 32 in
Advanced games.) It will probably confuse the parser if any of these
names is something like `"my"', `"the"', `"all"', `"except"' or
`"this"'. Numbers can safely be used, however.
!!!!! In fact, `name' contains a list of byte addresses of dictionary
words. A quick way to print out the first word in the `name' list is
therefore
print_addr obj.name;
!!!!! The attributes and so on can be taken away as well as added, thus:
...
has light ~scored;
which is sometimes useful to over-ride an inheritance from a class
definition.
!!!!! A final curiosity of the `Object' syntax is that objects can be
given more than one name. This is a hangover from much earlier days of
Inform, to do with re-using the same physical object in different
logical ways, something which the author does not commend to anyone.
4 Places, scenery and the map
*****************************
It was a long cylinder of parchment, which he unrolled and spread
out on the floor, putting a stone on one end and holding the
other. I saw a drawing on it, but it made no sense.
- John Christopher, `The White Mountains'
And if no piece of chronicle we prove,
We'll build in sonnets pretty rooms;
As well a well wrought urn becomes
The greatest ashes, as half-acre tombs.
-- John Donne (1571?--1631), `The Canonization'
4.1 Joining rooms together
==========================
Back to our example. Throw away the old `blank room' and replace it by
Object Square_Room "Square Room"
with description "A broad, square room, ten yards on a side, \
floored with black and white chequered tiles."
has light;
(We also have to change the `Initialise' routine to make this the place
where the player begins, since the Blank Room no longer exists.)
Like the blank room, this one has `light'. (If it didn't, the player
would never see it, since it would be dark, and the player hasn't yet
been given a lamp or torch of some kind.) So where is the light coming
from?
Nearby chandelier "crystal chandelier"
with name "crystal" "chandelier",
initial "A crystal chandelier hangs from far above, casting \
light in little rainbows across the floor.",
description "The crystal is beautiful cut-glass."
has static;
This is part of the fittings, hence the `static' attribute (which means
it can't be taken or moved). But what about the rainbows?
Nearby rainbows "rainbows"
with name "rainbow" "rainbows",
description "Caused by diffraction, or something like that - \
you were never very good at physics."
has scenery;
Being `scenery' makes the object not only static but also not described
by the game unless actually examined by the player. A true
perfectionist might alter it to:
Nearby rainbows "rainbows"
with name "rainbow" "rainbows",
description "Caused by diffraction, or something like that - \
you were never very good at physics.",
before [;
Take, Push, Pull, Turn: "But the rainbows are made of light.";
],
has scenery;
Let us now add a second room:
Object Corridor "Sloping Corridor"
with description "This corridor slopes upward and out of the \
square room.",
d_to Square_Room, s_to Square_Room,
u_to "The slope becomes impossibly steep, and you retreat.",
cant_go "The corridor runs up and down."
has light;
and extend the Square Room to:
Object Square_Room "Square Room"
with description "A broad, square room, ten yards on a side, \
floored with black and white chequered tiles. A doorway \
in the centre of the north side opens onto a rising \
corridor.",
u_to Corridor,
n_to Corridor
has light;
The player can now go from one to the other. The properties `u_to',
`d_to', `n_to' (and so on) declare what lies in the directions `up',
`down', `north' (and so on). If they aren't declared, one cannot go
that way. Notice that they can be either a room or a message which is
printed if the player tries to go in the given direction.
In the Square Room, if the player tries to go, say, east, she gets a
message along the lines of `You can't go that way.', which is not very
helpful. In the Corridor, the `cant_go' message is printed instead.
(In fact, as is often the case with properties, instead of giving an
actual message you can instead give a routine to print one out, so that
what is printed varies with circumstances.)
Map connections are all one-way, in themselves: they just happen to be
defined here so that they appear two-way.
4.2 Adding code to rooms
========================
Rooms also have rules of their own. We might write:
Object Corridor "Sloping Corridor"
with description "This corridor slopes upward and out of the \
square room: the floor underfoot is a little sticky.",
d_to Square_Room, s_to Square_Room,
u_to "The slope becomes impossibly steep, and you retreat.",
cant_go "The corridor runs up and down.",
before [;
Take:
if (noun == cone)
"The cone seems to be stuck to the floor here.";
],
has light;
and now the cone (if dropped there) cannot be taken from the floor of
the Sloping Corridor. The variables `noun' and `second' hold the first
and second nouns supplied with an action. Rooms have `before' and
`after' routines just as objects do.
!!!!! Sometimes the room may be a different one after the action has
taken place. The `Go' action, for instance, is offered to the `before'
routine of the room which is being left, and the `after' routine of the
room being arrived in. For example:
after [;
Go:
if (noun==in_obj)
print "How grateful you are to get out of the \
rain...^";
],
will print the message when the room is entered via the `in' direction.
(Note that the message is printed with the `print' command. This means
that it does not automatically return true: in fact, it returns false,
so the game knows that the usual rules still apply. Also, no new-line
is printed automatically: but the `^' symbol means `print a new-line',
so one is actually printed.)
!!!! Directions (such as `north') are objects called `n_obj', `s_obj'
and so on: in this case, `in_obj'. (They are not to be confused with
the property names `n_to' and so on.) Moreover, you can change these
directions (as far as Inform is concerned, only things in the special
object `compass' can be directions).
4.2.1 Exercise: world colours
-----------------------------
!!!! In the first millenium A.D., the Mayan peoples of the Yucatán
Peninsula had `world colours' white (*sac*), red (*chac*), yellow
(*kan*) and black (*chikin*) for what we call the compass bearings
north, east, south, west (for instance west is associated with
`sunset', hence black, the colour of night). Implement this.
*Note Answer (world colours)::.
4.2.2 Exercise: reflecting the map
----------------------------------
!!!! (Cf. `Trinity'.) How can the entire game map be suddenly
east-west reflected?
*Note Answer (reflecting the map)::.
4.2.3 Exercise: reflecting directions
-------------------------------------
!!!!! Even when the map is reflected, there may be many room
descriptions referring to `east' and `west' by name. Reflect these too.
*Note Answer (reflecting directions)::.
5 Causing actions and making new ones
*************************************
Only the actions of the just
Smell sweet and blossom in their dust.
-- James Shirley (1594--1666), `The Contention of Ajax and Ulysses'
...a language obsessed with action, and with the joy of seeing
action multiply from action, action marching relentlessly ahead
and with yet more actions filing in from either side to fall into
neat step at the rear, in a long straight rank of cause and
effect, to what will be inevitable, the only possible end.
- Donna Tartt, `The Secret History'
5.1 Causing actions to happen
=============================
One often wants to simulate the effect of a player typing something.
For instance, in the Pepper Room the air is full of pepper and every
turn the player sneezes and drops something at random. If the code to
do this simply removes an object and puts it on the floor, then it might
accidentally provide a solution to a problem like `the toffee apple
sticks to your hands so you can't drop it'.
This can at least be coded like this:
You sneeze convulsively, and lose your grip on the toffee apple...
The toffee apple sticks to your hand!
which (though not ideal) is much better. As an example, here is another
piece of scenery to clutter up the tiny map so far:
Object low_mist "low mist"
with name "low" "swirling" "mist", article "the",
initial "A low mist swirls about your feet.",
description "It carries the unmistakable odour of cinnamon.",
found_in Square_Room Corridor,
before [;
Smell: <<Examine self>>;
],
has static;
This mist is found in both the Square Room and the Corridor: note that
the `found_in' property has a list of places as its value.
The player will find that smelling the mist produces the same message as
looking at it. The command
<<Examine self>>;
causes the game to behave exactly as if the player had typed `examine
the mist' at the keyboard: that is, the `Examine' action happens,
applied to the `low_mist' object. (`self' always means the object
whose routine this is. In this case, it's really a bit pointless since
<<Examine low_mist>>;
does just the same thing.) After going through the business of
examining the mist, the routine then returns `true', or 1 (in this case,
so that the normal rules for smelling something are stopped in their
tracks).
If instead
<Examine self>;
had been used, the same would have happened, but the routine would not
have been returned from. So the mist would be examined; and then the
routine for `Smell' would resume, and since there's nothing more to it,
it would return false; whereupon the library's usual rules would make
it say something like `You smell nothing unusual.'.
All actions can be written this way:
<Look>; <<ThrowAt cone chandelier>>;
will, for instance, look around, then behave as if the player had asked
to throw the cone at the chandelier, then return true.
!!!! Internally, actions like `Look', `ThrowAt' and `Take' are stored
as numbers, and the number associated with an action can be got at by
`x = ##Take;' for instance. The variable `action' holds the current
action number and sometimes it's convenient to use this directly. For
instance, imagine a mirror hung very far above the player. Given:
before [;
if (action==##Examine) rfalse;
"The mirror is too high up, out of reach.";
]
the player will only be able to examine the mirror, and nothing else.
This prevents the library from ever saying something like `You push the
mirror but nothing happens.', which would be misleading.
!!!! An action corresponds to a routine somewhere which actually carries
out the looking, throwing at, taking and so forth. (Well, in fact there
are also some special actions called `fake actions' which don't
correspond to such routines, as we shall come to later.) These have the
same name as the action with `Sub' (short for `subroutine') on the end:
so, `TakeSub' for instance.
5.2 Actions defined by the library
==================================
!!!! The actions implemented by the library are in three groups. The
useful ones are in groups 2 and 3:
1. Quit, Restart, Restore, Verify, Save, ScriptOn, ScriptOff,
Pronouns, Score, Fullscore, LMode1, LMode2, LMode3, NotifyOn,
NotifyOff;
2. Inv, InvTall, InvWide, Take, Drop, Remove, PutOn, Insert, Transfer,
Empty, Enter, Exit, GetOff, Go, GoIn, Look, Examine, Give, Show,
Unlock, Lock, SwitchOn, SwitchOff, Open, Close, Disrobe, Wear, Eat;
3. Yes, No, Burn, Pray, Wake, WakeOther [person], Kiss, Think, Smell,
Listen, Taste, Touch, TouchThing, Dig, Cut, Jump [jump on the
spot], JumpOver, Tie, Drink, Fill, Sorry, Strong [swear word],
Mild [swear word], Attack, Swim, Swing [something], Blow, Rub,
Set, WaveHands [ie, just "wave"], Wave [something], Pull, Push,
PushDir [push something in a direction], Turn, Squeeze, LookUnder
[look underneath something], Search, ThrowAt, Answer, Buy, Ask,
Sing, Climb, Wait, Sleep
Of course, the player can type all manner of things to get these. For
instance, `take off shirt' and `remove the shirt' both cause the
`Disrobe' action. Your code can ignore this complication.
!!!!! Group 1 actions are called `meta' - they are outside the game
proper, and your code is unable to interfere with them. (If you want a
room where the game can't be saved, as for instance `Spellbreaker'
cunningly does, you'll have to tamper with `SaveSub' directly, using a
`Replace'd routine.)
Although all actions call the `before' routines, not all of them bother
to check `after' routines. For instance, since the built-in `SmellSub'
routine just says `You smell nothing out of the ordinary', there would
be no point calling `after' routines - nothing, after all, has been
done. (These are the group 3 actions above.)
!!!! The ones which actually do something, and call `after', are:
Inv, Take, Drop, Remove, PutOn, Insert, Exit, Go, Look, Examine,
Unlock, Lock, SwitchOn, SwitchOff, Open, Close, Disrobe, Wear, Eat,
Search.
!!!!! Some other group 2 actions use these `after' routines indirectly
- if the player empties a sack out onto the floor, this is deemed to be
a sequence of `Drop' actions, for instance, and if a sack is emptied
into a packing case this is considered a multiple insertion.
!!!!! `Search' (the searching or looking-inside-something action) is a
slightly special case between groups 2 and 3. It never actually does
anything beyond printing messages. What happens is that if it would be
sensible to look inside the object (i.e., if it's an open or
transparent container and there is light), `after' is called
beforehand. In this way you can use `before' to trap searching of
random scenery, and `after' to alter rules for listing contents of
containers.
5.3 Making new actions
======================
The library's actions are easily added to. For instance, add the
routine:
[ BlorpleSub;
"You speak the magic word ~Blorple~. Nothing happens.";
];
(somewhere after the `Initialise' routine, say, to be tidy). There is
now an action `Blorple' (though it doesn't do anything very
interesting). One can use the command `<Blorple>;' to make it happen,
and could change the `before' routine of, say, the Corridor, to make
`Blorple' do something exciting in that one place. In other words,
`Blorple' is now an action just like any other.
But the player can't yet type `blorple' and get this response, because
although the action exists, it hasn't been written into the grammar of
the game. The grammar is a large table (mostly written out in the
`Grammar' library file). It can easily be added to, and in this case
we simply add the lines
Verb "blorple"
* -> Blorple;
immediately after the inclusion of the `Grammar' file. (The spacing is
just a matter of convention.) This is about as simple as grammar lines
come, and means that only the word `blorple' can be used as a verb, and
it can't apply to any noun or nouns. (Far more about grammar later.)
!!!! It is time to be more precise about the exact sequence of events.
Suppose the player is in the Bedquilt Room, and types `drop oyster'.
Once it has checked that this is a reasonable command, the normal rules
can be interrupted at eight different stages:
1. Call `GamePreRoutine' (if there is one). If this returns true,
stop here.
2. Call the `before' of the player. If this returns true, stop here.
3. Call the `before' of Bedquilt Room. If this returns true, stop
here.
4. Then the `before' of the oyster. If this returns true, stop here.
5. Actually drop the object.
6. Call the `after' of the player. If this returns true, stop here.
7. Call the `after' of Bedquilt Room. If this returns true, stop here.
8. Then the `after' of the oyster. If this returns true, stop here.
9. Call `GamePostRoutine' (if there is one). If this returns true,
stop here.
10. Print `Dropped.'
!!!!! `GamePreRoutine' and `GamePostRoutine' are examples of `entry
points', like `Initialise': routines you can provide to make global
rule changes. Their use is a drastic measure to be avoided if
possible. The player's own `before' and `after' will be discussed in
\S 11.
!!!!! `Fake actions' are just like real actions, except that they don't
occur in any game grammar, so they're never generated by the parser.
They're a neat way to pass `messages' from one object to another one.
Because they aren't defined implicitly in the grammar, they have to be
explicitly declared before use, by the directive:
`Fake_Action' ACTION-NAME
5.3.1 Exercise: medicine bottle
-------------------------------
!!!!! How can you make a medicine bottle, which can be opened in a
variety of ways in the game, so that the opening-code only occurs in the
bottle definition?
*Note Answer (medicine bottle)::.
6 Containers, supporters and sub-objects
****************************************
The concept of a surface is implemented as a special kind of
containment. Objects which have surfaces on which other objects
may sit are actually containers with an additional property of
`surfaceness'.
- P. David Lebling, `Zork and the Future'
6.1 Introduction to containers
==============================
Objects can be inside or on top of one another. An object which has the
`container' attribute can contain things, like a box: one which has
`supporter' can hold them up, like a table. (An object can't have both
at once.) It can hold up to 100 items, by default: this is set by the
`capacity' property.
However, one can only put things inside a container when it has `open'.
If it has `openable', the player can open and close it at will.
(Unless it also has `locked'.)
To complicate matters, some containers are `transparent' (so that the
player can see inside them even when they are closed) and some are not.
Containers (and supporters) are able to react to things being put inside
them, or removed from them, by acting on the `Receive' and `LetGo'
actions.
!!!! `LetGo' and `Receive' are actually two of the fake actions: they
are the actions `Insert' and `Remove' looked at from the container's
point of view.
6.1.1 Exercise: two boxes
-------------------------
Make a glass box and a steel box, which behave differently when the lamp
is shut up inside them.
*Note Answer (two boxes)::.
6.1.2 Exercise: toothed bag
---------------------------
Make the following, rather acquisitive bag:
>put fish in bag
The bag wriggles hideously as it swallows the fish.
>get fish
The bag defiantly bites itself shut on your hand until you desist.
*Note Answer (toothed bag)::.
6.2 Containers which can be locked
==================================
Objects which have `locked' cannot be opened, be they doors or
containers (or both). But objects which have `lockable' can be locked
or unlocked with the appropriate key, which is declared in the
`with_key' property. (If it is undeclared, then no key will fit.)
As a final example of a container, this is a fairly typical locked
cupboard:
Nearby cupboard "bolted cupboard"
with name "bolted" "cupboard",
describe [;
if (self hasnt open)
"^A shut cupboard is bolted to one wall.";
"^Bolted up on one wall is an open cupboard.";
],
with_key key
has locked container openable lockable static;
6.3 Other uses for containers
=============================
Now suppose you want to make a portable television set which has four
different buttons on it. Obviously when the television moves, its
buttons should move with it, and the sensible way to arrange this is to
make the four buttons possessions of the `television' object.
However, the television isn't a `container', and the player can't
normally `see' (that is, refer to) the possessions of an object. So how
do we bring the buttons into the player's `view' without making them
removable, or allowing anyone to put extra things `into' the television?
This is what the `transparent' attribute is for: it is an extremely
useful device to allow the player to `see' (i.e., talk about) the
contents of an object.
6.3.1 Exercise: television set
------------------------------
Implement a television set with attached power button and screen.
*Note Answer (television set)::.
7 Doors
*******
Standing in front of you to the north, however, is a door
surpassing anything you could have imagined. For starters, its
massive lock is wrapped in a dozen six-inch thick iron chains. In
addition, a certain five-headed monster...
- Marc Blank and P. David Lebling, `Enchanter'
O for doors to be open and an invite with gilded edges
To dine with Lord Lobcock and Count Asthma.
-- W. H. Auden (1907--1973), `Song'
A useful kind of object is a `door'. This need not literally be a
door: it might be a rope-bridge or a ladder, for instance. To set up a
`door':
1. give the object the `door' attribute;
2. set the `door_to' property to the destination;
3. set the `door_dir' property to the direction which that would be,
such as `n_to';
4. make the room's map connection in that direction point to the door
itself.
For example, here is a closed and locked door:
Object In_Immense_N_S_Passage "Immense N/S Passage"
with description "One end of an immense north/south passage.",
s_to In_Giant_Room,
n_to RustyDoor;
Nearby RustyDoor "rusty door"
with description "It's just a big iron door.",
name "door" "hinge" "hinges" "massive" "rusty" "iron",
when_closed "The way north is barred by a massive, rusty, \
iron door.",
when_open "The way north leads through a massive, rusty, iron \
door.",
door_to In_Cavern_With_Waterfall,
door_dir n_to,
with_key set_of_keys
has static door openable lockable locked;
(Note that the door is `static' - otherwise the player could pick it up
and walk away with it!) The properties `when_closed' and `when_open'
give descriptions appropriate for the door in these two states.
Doors are rather one-way: they are only really present on one side. If
a door needs to be accessible (openable and lockable from either side),
a neat trick is to make it present in both locations and to fix the
`door_to' and `door_dir' to the right way round for whichever side the
player is on. Here, then, is a two-way door:
Object Grate "steel grate"
with name "grate" "lock" "gate" "grille" "metal" "strong" "steel"
"grating",
description "It just looks like an ordinary grate mounted in \
concrete.",
with_key set_of_keys,
door_dir [;
if (location==Below_The_Grate) return u_to; return d_to;
],
door_to [;
if (location==Below_The_Grate) return Outside_Grate;
return Below_The_Grate;
],
describe [;
if (self has open) "^The grate stands open.";
if (self hasnt locked) "^The grate is unlocked but shut.";
rtrue;
],
found_in Below_The_Grate Outside_Grate
has static door openable lockable locked;
where `Below_The_Grate' has `u_to' set to `Grate', and `Outside_Grate'
has `d_to' set to `Grate'. The grate can now be opened, closed,
entered and locked from either above or below.
!!!! At first sight, it isn't obvious why doors have the `door_dir'
property. Why does a door need to know which way it faces? The idea is
that if there's an open door in the south wall, a player can go through
it either by typing `south' or `enter door'. So what the `Enter'
action does (provided the door is actually open) is to cause the `Go'
action with the given direction.
!!!! This has one practical consequence: if you put `before' and
`after' routines on the `Enter' action for the `Grate', they only apply
to a player typing `enter grate' and not to one just typing `down'.
The way to trap both at once is to write a routine for the `d_to'
property of `Outside_Grate'.
8 Switchable objects
********************
In one corner there is a machine which is reminiscent of a clothes
dryer. On its face is a switch which is labelled `START'. The
switch does not appear to be manipulable by any human hand (unless
the fingers are about 1/16 by 1/4 inch)...
- `Zork'
Objects can also be `switchable'. This means they can be turned off or
on, as if they had some kind of switch on them. The object has the
attribute `on' if it's on. For example:
Object searchlight "Gotham City searchlight" skyscraper
with name "search" "light" "template", article "the",
description "It has some kind of template on it.",
when_on "The old city searchlight shines out a bat against \
the feather-clouds of the darkening sky.",
when_off "The old city searchlight, neglected but still \
functional, sits here."
has switchable static;
Here is a lamp which provides for batteries which will some day run
down, though the code to actually deplete these batteries (by
decrementing `time_left' every turn it's switched on) is omitted:
Nearby brass_lantern "brass lantern"
with name "lamp" "lantern" "shiny" "brass",
when_off "There is a shiny brass lamp nearby.",
when_on "Your lamp is here, gleaming brightly.",
time_left 330,
before [;
Examine:
print "It is a shiny brass lamp";
if (brass_lantern hasnt on) ". It is not currently lit.";
if (brass_lantern.time_left < 30) ", glowing dimly.";
", glowing brightly.";
Burn: <<SwitchOn brass_lantern>>;
Rub: "Rubbing the electric lamp is not particularly \
rewarding. Anyway, nothing exciting happens.";
SwitchOn:
if (brass_lantern.time_left <= 0)
"Unfortunately, the batteries seem to be dead.";
],
after [;
SwitchOn: give brass_lantern light;
SwitchOff: give brass_lantern ~light;
],
has switchable;
9 Things to enter, travel in and push around
********************************************
...the need to navigate a newly added river prompted the invention
of vehicles (specifically, a boat).
- P. David Lebling, Marc Blank and Timothy Anderson
Some objects in a game are `enterable', which means that a player can
get inside them. The idea of `inside' here is that the player is only
half-in, as with a car or a psychiatrist's couch. (If it's more like a
prison cell, then it should be a separate place.) In practice one
often wants to make an `enterable' thing also a `container', or, as in
this example, a `supporter':
Object chair "dentist's chair" surgery
with name "dentists" "chair" "couch",
has enterable supporter;
All the classic games have vehicles (like boats, or fork lift trucks, or
hot air balloons) which the player can journey in, so Inform makes this
easy. Here is a simple case:
Object car "little red car" cave
with name "little" "red" "car",
description "Large enough to sit inside. Among the controls \
is a prominent on/off switch. The numberplate is KAR 1.",
when_on "The red car sits here, its engine still running.",
when_off "A little red car is parked here.",
before [;
Go:
if (car has on) "Brmm! Brmm!";
print "(The ignition is off at the moment.)^";
],
has switchable enterable static container open;
Actually, this demonstrates a special rule. If a player is inside an
`enterable' object and tries to move, say `north', the `before' routine
for the object is called with the action `Go', and `n_obj' as the noun.
If it returns false, the game disallows the attempt to move (as
usual). If it returns true, then the vehicle and player move together
via the game's usual map.
!!!! Because you might want to drive the car `out' of a garage, the
`out' verb does not make the player get out of the car. Usually the
player has to type something like `get out' to make this happen, though
of course the rules can be changed.
!!!! Objects like the car (if the hand brake is off) or, say, an
antiquated wireless on casters, are obviously too heavy to pick up but
the player should at least be able to push them from place to place.
When the player tries to do this, the `PushDir' action is generated.
Now, if the `before' routine returns false, the game will just say that
the player can't; and if it returns true, the game will do nothing at
all, guessing that the `before' routine has already printed something
more interesting. So how does one actually tell Inform that the push
should be allowed? The answer is that one has to do two things: call
the `AllowPushDir()' routine (a library routine), and then return true.
So, for example,
Nearby huge_ball "huge plaster-of-paris ball"
with name "huge" "plaster" "ball",
description "A good eight feet across, though fairly \
lightweight.",
initial "A huge plaster-of-paris ball rests here, eight feet \
wide.",
before [;
PushDir: AllowPushDir(); rtrue;
Take, Remove: "There's a lot of plaster in an eight-foot \
sphere.";
],
after [;
PushDir: "The ball rattles about and is hard to stop once \
underway.";
],
has static;
9.0.1 Exercise: the red car
---------------------------
Alter the car so that it won't go up or down stairs.
*Note Answer (the red car)::.
10 Living creatures and conversation
************************************
To know how to live is my trade and my art.
-- Michel de Montaigne (1533--1592), `Essays'
Everything that can be said can be said clearly.
-- Ludwig Wittgenstein (1889--1951), `Tractatus'
This rummage through special kinds of objects finishes up with the most
sophisticated kind: living ones. Animate objects (such as sea monsters,
mad aunts or nasty little dwarves) have a property called `life',
containing their rules. This behaves just like a `before' or `after'
routine, but only the following actions apply:
`Attack'
The player is making hostile advances...
`Kiss'
... or excessively friendly ones.
`ThrowAt'
The player asked to throw `noun' at the creature.
`Give'
The player asked to give `noun' to the creature...
`Show'
... or just to show it.
`Ask'
The player asked the creature about something: `noun' holds the
word.
`Answer'
The player tried either of:
`answer WORD to troll'
`trool, SOMETHING NOT UNDERSTOOD'
In either case the variable `special_word' is set to the dictionary
entry of the first word, or 0 if it isn't in the dictionary, and
`special_number' is set to an attempt to read it as a number. (For
instance, `computer, 143' will cause `special_number' to be set to
143.)
`Order'
On the other hand, an `Order' happens when the parser does
understand what the player wants the creature to do: e.g., `troll,
go south'. `action', `noun' and `second' are set up as usual: in
this case `action=##Go' and `noun=s_obj', while `second=0'.
If the routine doesn't exist, or returns false, events will take their
usual course. Here is a full example:
Object snake "sullen snake" mists
with name "sullen" "snake",
description "Perhaps a boa constrictor. Perhaps not.",
life [;
Order:
if (action==##Go)
"The snake wasn't born yesterday.";
Attack: "Lazily, the snake dodges your attack.";
Kiss: "What a repulsive idea.";
ThrowAt:
print "Effortlessly, the snake dodges ";
DefArt(inp1); ".";
Answer, Show: "The snake disdains to comment.";
Ask:
if (noun == 'mists')
"~Healthy and good for the skin, mists.~";
"~I'm only the obligatory monster.~";
Give:
if (noun has edible) {
remove noun;
"~Mmm! Thanks! I still hate you, though.~";
}
"~Bleurghh! Are you trying to poison me?~";
],
has animate;
Of course an `animate' still has `before' and `after' routines like any
other, so you can trap many other kinds of behaviour. (The library
understands that, for example, an `animate' creature cannot be taken.)
!!!! `DefArt' is a routine which prints the short name of an object
along with its definite article, usually `the'.
Sometimes creatures should be `transparent', sometimes not. Consider
these two cases of `animate' characters, for instance:
* an urchin with something bulging inside his jacket pocket;
* a hacker who has a bunch of keys hanging off his belt.
The hacker is `transparent', the urchin not. That way the parser
prevents the player from referring to whatever the urchin is hiding
(even if the player has played the game before, and knows what is in
there and what it's called). But the player can look at and be
tantalised by the hacker's keys.
!!!! Another way to trap some of these actions (those which do not
involve conversation, i.e., other than `Order', `Answer' or `Ask') is
to use `before' in the ordinary way. This lets you change rules about,
say, giving things to people in particular places. The `ThrowAt'
action is also an ordinary one (for coconut shies, greenhouse windows
and the like) but is given to `life' as well because most creatures
react to it, and it's a convenience to have all the rules for a
creature in one place.
!!!! A footnote about `Order': this is another `fake action'. The
`before' and `after' routines of a room can't detect the player having
given a request to another character. Also, if you want the snake to
obey when the player tells it to take something, you have to write the
code to do the actual taking yourself. This isn't any problem in
practice. (Try looking at the code for `Christopher' in the `Toyshop'
example game.)
11 Starting, moving, changing and killing the player
****************************************************
There are only three events in a man's life; birth, life and death;
he is not conscious of being born, he dies in pain and he forgets
to live.
- Jean de la Bruyère (1645-1696)
That is the road we all have to take - over the Bridge of Sighs
into eternity.
- Søren Kierkegaard (1813-1855)
The player normally begins in the room which `location' is set to, and
setting `location' is the only absolute obligation on a game's
`Initialise' routine. (The room may be initially dark if you so
choose, or rather if you provide no light source.) In fact `location'
could be set to a chair or bed just as easily if the player is to start
seated or lying down. If you would like to give the player some items
to begin with, `Initialise' should also `move' them to `player'.
To move the player about (for teleportation of some kind), two things
must be done: to move the player object, by
move player to newroom;
and also to change the `location' variable, which says which room to
display on the status line:
location = newroom;
The cleanest way to move the player is call `PlayerTo(place);' which
also sorts out things like only printing the new room's description if
there's enough light there to see by.
!!!! In general `location' can be different from `parent(player)' in
two ways: it can be `Darkness' (that is, the special object `thedark',
which behaves somewhat like a room), or it can be the actual room while
`parent(player)' is something the player sits inside, like (say) a jeep.
!!!! Calling `PlayerTo(place, 1);' will move the player without
printing anything, and in particular without printing any room
description.
The player's whole persona can easily be changed, by setting
player.before = #r$MyNewRule;
where `MyNewRule' is a new `before' rule to be applied to every action
of the player's (or similarly for an `after' rule). For instance, if a
cannon goes right next to the player, a period of deafness might ensue,
and this rule could stop the `Listen' action from taking its normal
course.
!!!! In fact a much more powerful trick is available: the `player' can
actually become a different character in the game, allowing the real
player at the keyboard to act through someone else. Calling
`ChangePlayer(obj)' will transform the player to `obj'. There's no
need for `obj' to have names like `me' or `myself'; the parser
understands these words automatically to refer to the
currently-inhabited `player' object. However, it must provide a
`number' property (which the library will use for workspace). The
maximum number of items the player can carry as that object will be its
`capacity'. Finally, since `ChangePlayer' prints nothing, you may want
to conclude with a `<<Look>>;'
`ChangePlayer' has many possible applications. The player who tampers
with Dr Frankenstein's brain transference machine may suddenly become
the Monster strapped to the table. A player who drinks too much wine
could become a `drunk player object' to whom many different rules
apply. The `snavig' spell of `Spellbreaker', which transforms the
player to an animal like the one cast upon, could be implemented thus.
More ambitiously, a game could have a stock of half a dozen main
characters, and the focus of play can switch between them. A player
might have a team of four adventurers to explore a dungeon, and be able
to switch the one being controlled by typing the name. (In this case,
an `AfterLife' routine - see below - may be needed to switch the focus
back to a still-living member of the team after one has met a sticky
end.)
!!!! Calling `ChangePlayer(object,1);' will do the same but make the
game print `(as Whoever)' during room descriptions.
The player is still alive for as long as the variable `deadflag' is
zero. When set to 1, the player dies; when set to 2, the player wins;
and all higher values are taken as more exotic forms of death. Now
Inform does not know what to call these exotica: so if they should
arise, it calls the `DeathMessage' routine, which is expected to look
at `deadflag' and can then print something like `You have changed'.
Many games allow reincarnation (or, as David M. Baggett points out, in
fact resurrection). You too can allow this, by providing an
`AfterLife'. This routine gets the chance to do as it pleases before
any `You are dead' type message appears, including resetting `deadflag'
back to 0 - which causes the game to proceed in the normal way, rather
than end. `AfterLife' routines can be tricky to write, though, because
the game has to be set to a state which convincingly reflects what has
happened. (For instance, try collapsing the bridge in `Advent' by
leading the bear across it, then being reincarnated and returning to
the scene.)
11.0.1 Exercise: *wayhel*
-------------------------
!!!! In Central American legend, a sorceror can transform himself into a
*nagual*, a familiar such as a spider-monkey; indeed, each individual
has an animal self or *wayhel*, living in a volcanic land over which
the king, as a jaguar, rules. Turn the player into his *wayhel*.
*Note Answer (wayhel)::.
12 Printing out names of objects
********************************
And we were angry and poor and happy,
And proud of seeing our names in print.
-- G. K. Chesterton (1874-1936), `A Song of Defeat'
Inform has a special form of print command for this, `print object',
but *do not use it*. Instead, call one of the following library
routines:
`DefArt(obj)'
Print the object with its definite article
`CDefArt(obj)'
The same, but capitalised
`InDefArt(obj)'
Print the object with indefinite article
`PrintShortName(obj)'
Print the object's short name alone
The indefinite article for an object is held in the property `article'
and is assumed to be `a' if none is declared. That means that if the
short name starts with a vowel, you need to set it to `an'. But
`article' offers much more amusement:
a platinum bar, an orange balloon, your Aunt Jemima,
some bundles of reeds, far too many marbles
are all examples of `article's.
Definite articles are always `the' unless an object is given the
attribute `proper', which makes it a proper noun and so not take a
definite article at all. Thus
the platinum bar, Aunt Jemima, Elbereth
are all printed by `DefArt', the latter two being `proper'.
As we shall later see (*note Naming of names::.), changing the short
name is easy.
!!!!! The reason `print object' is unsafe is that it prints the real,
`hardware and unchangeable' short name, whereas we want everything to
work nicely when the user changes the short name of an object in play:
so the library routines almost all indirect through `PrintShortName'
(except in two cases to do with `ChangePlayer', since the current
player's short name is always `yourself').
12.0.1 Exercise: printing objects
---------------------------------
!!!! Why does
print CDefArt(obj), " falls to the floor.^";
seem to work, but mysteriously print the number 1 after the name of the
object?
*Note Answer (printing objects)::.
13 Classes of objects
*********************
In most games there are groups of objects with certain rules in common.
As well as individual objects, Inform allows one to define classes in
almost exactly the same way. The only difference between the layout of
a class and object definition is that a class has no short name or
initial location (since it does not correspond to a single real item).
For example:
Class Treasure
with depositpoints 10,
after [;
Take:
if (location==Inside_Building)
score=score-self.depositpoints;
score=score+5;
"Taken!";
Drop:
score=score-5;
if (location==Inside_Building) {
score=score+self.depositpoints;
"Safely deposited.";
}
],
has valuable;
An object of this class inherits the properties and attributes it
defines: in this case, an object of class `Treasure' picks up the given
score and rules automatically. So
Nearby bars_of_silver "bars of silver"
class Treasure
with description "They're probably worth a fortune!",
initial "There are bars of silver here!",
article "some",
name "silver" "bars";
inherits the `depositpoints' value of 10 and the rules about taking and
dropping. If the silver bars had themselves set `depositpoints' to 15,
say, then the value would be 15: i.e., the class would be over-ridden.
!!!! `depositpoints' isn't a library property: it's one defined in the
game this example is drawn from, the `Advent' example game.
We could also, for instance, have:
Nearby cake "valuable cake"
class Treasure
with description "Exquisite!",
initial "There's a valuable cake here!",
after [;
Eat: "Your most expensive meal in ages, but worth it.";
],
name "valuable" "cake"
has edible;
Now the cake has two `after' rules. Both apply, but the rule in the
cake itself takes precedence, i.e., happens first.
!!!! An object can inherit from several classes at once. Moreover, a
class can itself inherit from other classes, so one can easily make a
class for `like Treasure but with only 8 `depositpoints''.
!!!!! The `class' field of an object definition contains a list of
classes,
`class' C1 ... Cn
in which case the object inherits first from C1, then from C2 and so on.
C2 over-rides C1 and so on along the line. These classes may well
disagree with each other, so the order matters. If C1 says
`depositpoints' is 5, C3 says it is 10 but the object definition itself
says 15 then the answer is 15.
!!!!! With some properties, the value is not replaced but added to: this
is what happened with `after' above. These properties are those which
were declared as `additive', e.g., by
Property additive before $ffff;
For instance, the standard Inform properties `name' and `before' are
both additive. So we could add `name "treasure",' to the properties in
the class definition for `Treasure', and then all objects of that class
would respond to the word `treasure', as well as their own particular
names.
14 Daemons and the passing of time
**********************************
Some, such as Sleep and Love, were never human. From this class an
individual daemon is allotted to each human being as his `witness
and guardian' through life.
- C. S. Lewis (1898-1963), `The Discarded Image'
Some daemon stole my pen (forgive th' offence)
And once betrayed me into common sense.
-- Alexander Pope (1688--1744), `The Dunciad'
14.1 Daemons (routines that run each turn)
==========================================
By tradition, a daemon is an event which happens each turn while it is
`active'. The classic example is of a dwarf which appears in the cave:
it has a daemon routine attached for moving about, throwing knives at
the player and other pleasantries.
Each object can have a `daemon' of its own. This is set going, and
stopped again, by calling the (library) routines
StartDaemon(the-object);
StopDaemon(the-object);
Once active, the `daemon' property of the object is called as a routine
each turn. Daemons are often started by a game's `Initialise' routine,
and active throughout.
!!!! Be warned: this continues to happen even if the daemon is
associated with a room or item which has been left behind by the player.
Actually this is very useful, as it means daemons can be used for
`tidying-up operations', or for the consequences of the player's actions
to catch up with him.
Daemons often run a fair amount of code. (There are plenty of good
examples in `Toyshop' and `Advent'.) They shouldn't be ridiculously
slow if they will run more than once. And some daemons so sit in the
background for enormously long times: for instance, the daemon in
`Advent' which hides until the player has managed to get all the
treasures, then pounces. Such daemons ought to check their condition
and return as quickly as possible if it fails.
14.1.1 Exercise: the thief
--------------------------
Many games contain `wandering monsters', characters who walk around the
map (usually hand-coded and not moving far abroad). Use a daemon to
implement one who wanders as freely as the player, like the thief in
`Zork I'.
*Note Answer (the thief)::.
14.1.2 Exercise: weights
------------------------
Use a background daemon to implement a system of weights, so that the
player can only carry a certain weight before her strength gives out and
she is obliged to drop something. It should allow for feathers to be
lighter than lawn-mowers.
*Note Answer (weights)::.
14.2 Timers and fuses
=====================
A `timer' (these are traditionally called `fuses' but the author can
stand only so much tradition) can alternatively be attached to an
object. (An object can't have both a `timer' and a `daemon' going at
the same time.) A timer is started with
StartTimer(the-object, time);
in which case it will `go off' (alarm clock-style) in the given number
of turns. This means that its `time_out' property will be called (as a
routine), once and once only, when the time comes.
It can be deactivated (so that it will never go off) by calling
StopTimer(the-object);
A timer is *required* to provide a `time_left' property, to hold the
amount of time left. If it doesn't, the library will print an error
message at run-time. You can alter `time_left' yourself, but setting
it to 0 does not stop the timer: use the routine `StopTimer' for that.
!!!! In early releases of the library, a `daemon' needed a `time_left'
as well, which was illogical and a nuisance: anyway, it's no longer the
case.
!!!! Normally, you can only have 32 timers or daemons active at the same
time as each other (there may be any number of inactive ones). But this
limit is easily raised: just define the constant `MAX_TIMERS' to some
larger value, putting the definition in your code before the `Parser'
file is included.
14.3 Routines that run when an object is in scope
=================================================
There is yet a third form of timed event. If a room provides an
`each_turn' routine, then this will be called in each turn when the
player is in it; if an object provides `each_turn', this is called
whenever the object is nearby. For instance, a radio might blare out
music whenever it is nearby; a sword might glow whenever monsters are
nearby; or a stream running through several forest locations might
occasionally float objects by.
`Each turn' is entirely separate from daemons and timers. Although an
object can't have both a timer and a daemon at the same time, it can
have an `each_turn' at the same time, and this is quite useful,
especially to run creatures. An ogre with limited patience can
therefore have an `each_turn' routine which worries the player (`The
ogre stamps his feet angrily!', etc.) while also having a timer set to
go off when his patience runs out.
!!!! `Nearby' actually means `in scope', a term which will be properly
explained later. The idea is based on line of sight, which works well
in most cases.
!!!!! But it does mean that the radio will be inaudible when shut up
inside most containers - which is arguably fair enough - yet audible
when shut up inside transparent, say glass, ones. You can always change
the scope rules using an `InScope' routine to get around this. In case
you want to tell whether scope is being worked out for ordinary parsing
reasons or instead for `each_turn' processing, look at the `et_flag'
variable (0 in the former case, 1 in the latter). Powerful effects are
available this way - you could put the radio in scope within all nearby
rooms so as to allow sound to travel. Or you could make a thief
audible throughout the maze he is wandering around in, as in `Zork I'.
The library also has the (limited) ability to keep track of time of day
as the game goes on.
If you're writing a game with the time instead of the score and turns on
the status line, you can set the time by
`SetTime(' 60 * HOURS + MINUTES`,' RATE `);'
The current time is held in the variable `the_time' and runs on a
24-hour clock. The RATE controls how rapidly time is moving: a RATE of
0 means it is standing still (that is, that the library doesn't change
it: your routines still can). A positive RATE means that that many
minutes pass between each turn; and negative RATE means that many turns
pass between each minute.
The time still won't appear on the game's status line unless you set
Statusline time;
as a directive somewhere in your code. And remember to start off the
clock by calling `SetTime' in your `Initialise' routine, if you're
going to use it.
!!!! Exactly what happens at the end of each turn is:
1. The turns counter is incremented.
2. The 24-clock is moved on.
3. Daemons and timers are run (in no guaranteed order).
4. `each_turn' takes place for the current room, and then for
everything nearby (that is, in scope).
5. The game's global `TimePasses()' routine is called.
6. Light is re-considered (it may have changed as a result of events
since this time last turn).
The sequence is abandoned if at any stage the player dies or wins.
14.3.1 Exercise: passing midnight
---------------------------------
How could you make your game take notice of the time passing midnight,
so that the day of the week could be nudged on?
*Note Answer (passing midnight)::.
14.3.2 Exercise: suspended in mid-air
-------------------------------------
!!!! Suppose the player is magically suspended in mid-air, but that
anything let go of will fall out of sight. The natural way to code this
is to use a daemon which gets rid of anything it finds on the floor
(this is better than just trapping `Drop' actions because objects might
end up on the floor in many different ways). Why is using `each_turn'
better?
*Note Answer (suspended in mid-air)::.
14.3.3 Exercise: varying rates of time
--------------------------------------
How would a game work if it involved a month-long archaeological dig,
where anything from days to minutes pass between successive game turns?
*Note Answer (varying rates of time)::.
15 Adding verbs and grammar to the parser
*****************************************
Grammar, which can govern even kings.
-- Molière (1622--1673), `Les Femmes savantes'
Language disguises thought ... The tacit conventions on which the
understanding of everyday language depends are enormously
complicated.
- Ludwig Wittgenstein, `Tractatus'
The next few sections will delve deep into the parser. Inform goes to a
great deal of trouble to make the parser as `open-access' as possible,
because a parser cannot ever be general enough for every game without
being extremely modifiable. So there are very many ways to customise
the Inform parser, hopefully without the user needing to understand much
about how it works (because this is quite a long story).
15.1 Adding grammar for new verbs
=================================
The first essential requirement of any parser is to accept the addition
of the grammar for a new verb. In Inform code, grammar should appear at
the end of the source code for a game, and of course most of it is
written out in the `Grammar' header file, which all games using the
library include. After this inclusion, you can add extras.
The directive for this is called `Verb', and here's a typical example:
some of the library's grammar for `take':
Verb "take" "get" "pick" "lift"
* "out" -> Exit
* multi -> Take
* multiinside "from" noun -> Remove
* "in" noun -> Enter
* multiinside "off" noun -> Remove
* "off" held -> Disrobe
* "inventory" -> Inv;
This declares a verb, for which `take', `get' and so on are synonyms,
and which can take seven different courses.
!!!! It should be noted that these really are synonyms: the parser
thinks `take' and `get' are exactly the same. Sometimes this has odd
results, so that although `get in bed' is correctly understood as a
request to enter the bed, `take in washing' is misunderstood as a
request to enter the washing. You could either get around this by
writing separate grammars for the two nearly-synonyms, or can cheat and
see if the variable `verb_word=='take'' or `'get''. Mostly, though,
you don't mind if a few odd things are accepted by the parser: what
matters more is that sensible things are not rejected by it.
When it ploughs through what the player has typed, the parser tries to
match each line in turn, starting at the top. The first line will only
be matched if the player has typed something like `get out'. The
second line is more interesting: it allows the player to type a single
object or a list of objects. So the second line could be matched by
`take the banana'
`get all the fruit except the apple'
There need be no grammar at all after the verb: for example, a grammar
for `inventory' could be as simple as
Verb "invent" "inv" "i"
* -> Inv;
After the `->' in each line is the name of an action. This is how
actions are defined: they are the names which appear in grammar lines
like this one. Remember that if you do create an action this way, you
also have to write a routine to execute the action, even if it's one
which doesn't do very much. For instance:
[ XyzzySub; "Nothing happens."; ];
Verb "xyzzy"
* -> Xyzzy;
will make a new magic-word verb `xyzzy', which always says `Nothing
happens' - always, that is, unless some `before' rule gets there first,
as it might do in certain magic places. (The name of the routine is
always the name of the action with `Sub' appended.)
The new action `Xyzzy' is treated exactly like all the standard Inform
actions: `##Xyzzy' gives its action number, and you can write `before'
and `after' rules for it in `Xyzzy:' fields just as you would for, say,
`Take'.
15.2 Grammar tokens
===================
The individual words in the grammar (after the `*' and before the `->')
are called `tokens'. In increasing order of complexity, this is the
complete list:
`"WORD"'
that literal word only
`noun'
any object in scope
`held'
object held by the player
`creature'
an object in scope which is `animate'
`multi'
one or more objects in scope
`multiheld'
one or more held objects
`multiexcept'
one or more in scope, except the other
`multiinside'
one or more in scope, inside the other
`ATTRIBUTE'
any object in scope which has the attribute
`noun = ROUTINE'
any object in scope passing the given test
`scope = ROUTINE'
an object in this definition of scope
`special'
*any* single word or number
`number'
a number only
`ROUTINE'
any text accepted by the given routine
In the case of `noun = 'ROUTINE, the parser applies the following test:
the variable `noun' is set to the object in question, and the routine
is called. If it returns true, the parser accepts the object, and
otherwise it rejects it.
`number' matches any decimal number from 0 upwards (though it rounds
off large numbers to 10000), and also matches the numbers `one' to
`twenty' written in English.
The token `special' is now obselete.
For now, we shall take `in scope' to mean `visible to the player'. It
is quite important in some cases to know exactly what this means, so a
better definition will be given later.
The `held' token is convenient for two reasons. Firstly, many actions
only sensibly apply to things being held (such as `Eat' or `Wear'), and
using this token in the grammar you can make sure that the action is
never generated by the parser unless the object is being held. That
saves on always having to write `You can't eat what you're not holding'
code. Secondly, suppose we have grammar
Verb "eat"
* held -> Eat;
and the player types
`eat the banana'
while the banana is plainly in view, on a shelf, say. It would be
rather petty of the game to refuse on the grounds that the banana is not
being held. So the parser will generate a `Take' action for the banana
and then, if the `Take' action succeeds, an `Eat' action. (Notice that
the parser does not just pick up the object, but issues an action in
the proper way - so if the banana had rules making it too slippery to
pick up, it won't be picked up.)
The `multi-' tokens indicate that a list of one or more objects can go
here. (The parser works out all the things the player has asked for,
sorting out plural nouns and words like `except' by itself, and then
generates actions for each one.) `multiexcept' is provided to make
commands like
`put everything in the rucksack'
parsable: the `everything' is matched by all of the player's possessions
except the rucksack (this stops the parser from generating an action to
put the rucksack inside itself). Similarly `multiinside' handles:
`remove everything from the cupboard'
A restriction here is that a single grammar line can only contain one
`multi-' token: so `hit everything with everything' can't be parsed
(straightforwardly: you can parse anything with a little more effort).
On the whole, this is no bad thing.
The reason not all nouns can be multiple in the first place is that too
helpful a parser makes too easy a game. You probably don't want to
allow something like
`unlock the mystery door with all the keys'
- you want the player to suffer having to try them one at a time, or
else to be thinking. (Of course if you do want to allow this it is easy
enough to change the grammar: the point is that you have the option.)
We can also sort out objects according to attributes that they have:
Verb "use" "employ" "utilise"
* edible -> Eat
* clothing -> Wear
... and so on ...
* enterable -> Enter;
and this is how attributes are used as tokens. (The library grammar
does not contain such an appallingly convenient verb!)
Since you can define your own attributes, it is therefore easy to make a
grammar line which matches only your own class of object.
!!!! A footnote: the `creature' token is thus equivalent to writing
`animate'; but it dates back to the earliest days of Inform, and does
no harm.
Sometimes even that isn't flexible enough. Here is a verb, `free',
which should only apply to animal kept in a cage:
[ CagedCreature;
if (noun in wicker_cage) rtrue; rfalse;
];
Verb "free" "release"
* noun=CagedCreature -> FreeAnimal;
So that only nouns which pass the `CagedCreature' test are allowed.
(The `CagedCreature' routine can appear anywhere in the code, though
it's tidier to keep it nearby.)
15.3 Parsing numbers
====================
So far, all the tokens were to tell the parser which of the objects in
scope were acceptable. Exactly what `in scope' means will be gone into
later, and so will the powerful `scope='... token. Here we next want
the parser to match things other than names of objects and prepositions
like `into'. The simplest useful case is of numbers, for example:
Verb "type"
* number -> TypeNum;
so that the TypeNum action will happen if the player types `type 504' or
`type seventeen'.
!!!! In fact, you're allowed to provide your own number-parsing routine,
so many sneaky possibilities are open here - Roman numerals,
coordinates like `J4', long telephone numbers (which would be too large
to fit into integer variables in Inform), understanding English numbers
and so on. This takes the form
[ ParseNumber buffer length;
! returning 0 if no match is made, or the number otherwise
];
and examines the supposed `number' held at the byte address `buffer',
which is a row of ASCII characters of the given `length'. If you
declare a `ParseNumber' routine, then the parser will always try this
first when trying to decode something as a number (and if it fails will
fall back on its own number-parsing routine).
!!!!! For not-very-good internal reasons, `ParseNumber' can't return 0
to mean the number zero. Probably `zero' won't be needed too often,
but if it is you can always return some value like 1000 and code the
verb in question to understand this as 0.
!!!!! You can give a routine to parse anything:
[ French w n; w=NextWord();
if (w=='un' or 'une') n=1;
if (w=='deux') n=2;
if (w=='trois') n=3;
if (w=='quatre') n=4;
if (w=='cinq') n=5;
if (n==0) return -1;
parsed_number = n; return 1;
];
will detect low numbers in French, and could be used by something like:
Verb "type"
* French -> TypeFrenchNum
* number -> TypeNum;
The specification for such a routine is as follows: it is to use
`NextWord' (possibly several times) to look at the appropriate words
which the user has typed. The variable `wn' (current word number) may
also be helpful. The routine must return
`-1'
if the user's input isn't understood,
`1'
if there is a numerical value resulting, or
`n'
if object number n is understood.
In the case of a number, the actual value should be put into the
variable `parsed_number'. On an unsuccessful match (returning -1) it
doesn't matter what the final value of `wn' is. On a successful one it
should be left pointing to the next thing *after* what the routine
understood. (Since `NextWord' moves `wn' on by one each time it is
called, this happens automatically unless the routine has read too far.)
15.3.1 Exercise: footnotes
--------------------------
(A beautiful feature stolen from David M. Baggett's game `The Legend
Lives', which uses it to great effect.) Some games produce footnotes
every now and then. Arrange matters so that these are numbered `[1]',
`[2]' and so on in order of appearance, to be read by the player when
`footnote 1' is typed.
*Note Answer (footnotes)::.
15.4 Extending the library grammar
==================================
That's all about tokens: back to verb definition commands, because some
of the above examples are slightly contrived - they create wholly new
and unlikely verbs. More often, one would want, say, a big array of
labelled buttons, any of which could be pushed. So the verb for `push'
ought to be extended, and for this the `Extend' directive is provided:
Extend "push"
* Button -> PushButton;
A routine called `Button' could then be written to accept things like
`button j16', `d11', `a5 button'.
The point of `Extend' is that it is against the spirit of the library
to alter the standard library files - including the grammar table -
unless absolutely necessary.
!!!!! Actually, there's a better way to provide these buttons, so that
they work with any verb automatically, and that's to make a button
object with a `parse_name' routine which cunningly extracts the button
number as it parses: see the game `Balances' for an example of this.
Normally, extra lines of grammar are added at the bottom of those
already there. This may not be what you want. For instance, `take' has
a grammar line
* multi -> Take
quite early on. So if you want to add a grammar line which diverts
`take something-edible' to a different action, like so:
* edible -> Eat
then it's no good adding this at the bottom of the `Take' grammar,
because the earlier line will always be matched first. Thus, you really
want to insert your line at the top, not the bottom, in this case. The
right command is
Extend "take" first
* edible -> Eat;
You might even want to actually replace the old grammar completely, not
just add a line or two. For this, use
Extend "push" replace
* Button -> PushButton;
and now `push' can be used only in this way. To sum up, `Extend' can
take three keywords:
`replace'
completely replace the old grammar with this one
`first'
insert the new grammar at the top of the old one
`last'
insert the new grammar at the bottom of the old one
with `last' being the default (which doesn't need to be said
explicitly).
15.5 Meta verbs and unknown verbs
=================================
!!!! Another convenience is to define `meta' verbs - verbs which are
not really part of the game, such as `save', `score' and `quit'. (You
probably want to add your own debugging commands as meta-verbs: if you
make them ordinary verbs, then they may work most of the time, but
might be interfered with by game rules, and will take up `game time'.)
To declare a verb as `meta', just add the word after the command. For
instance, the library defines
Verb meta "score"
* -> Score;
The parser will match the grammar exactly in the normal way, but treats
the resulting action as outside the game - taking no time up, and
possible at any moment.
!!!!! There are (a few) times when verb definition commands are not
enough. For example, in the original `Advent' (or `Colossal Cave'),
the player could type the name of a not-too-distant place which had
previously been visited, and be taken there. There are several ways to
code this - say, with 60 rather similar verb definitions, or with a
single `travel' verb which has 60 synonyms, whose action routine looks
at the parser's `verb_word' variable to see which one was typed - but
here's another. The library will call the `UnknownVerb' routine (if
you provide one) when the parser can't even get past the first word.
This has two options: it can return false, in which case the parser
just goes on to complain as it would have done anyway. Otherwise, it
can return a verb word which is substituted for what the player
actually typed. Here is a foolish example:
[ UnknownVerb w;
if (w=='shazam') {
print "Shazam!^";
return 'inventory';
}
rfalse;
];
which responds to the magic word `shazam' by printing `Shazam!' and
then, rather disappointingly, taking the player's inventory. But in the
example above, it could be used to look for the word `w' through the
locations of the game, store the place away in some global variable,
and then return `'go''. The `GoSub' routine could then be fixed to
look at this variable.
!!!!! If you allow a flexible collection of verbs (say, names of spells
or places) then you may want a single `dummy' verb to stand for
whichever is being typed. This may make the parser produce strange
questions because it is unable to sensibly print the verb back at the
player, but you can fix this using the `PrintVerb' entry point (*note
Library entry points::.). See the exercise below.
15.5.1 Exercise: unknown verbs
------------------------------
!!!!! Why is it usually a bad idea to print text out in an `UnknownVerb'
routine?
*Note Answer (unknown verbs)::.
15.5.2 Exercise: control panel
------------------------------
!!!! A tricky one: code a spaceship whose control panel has five sliding
controls, each of which can be set to a numerical value, so that the
game looks like:
>look
Machine Room
There is a control panel here, with five slides, each of which can
be set to a numerical value.
>push slide one to 5
You set slide number 1 to the value 5.
>examine the first slide
Slide number 1 currently stands at 5.
>set four to six
You set slide number 4 to the value 6.
*Note Answer (control panel)::.
15.5.3 Exercise: quoted strings
-------------------------------
!!!!! An even trickier one: write a parsing routine which will accept
any amount of text (including spaces, full stops and commas) between
double-quotes as a single token.
*Note Answer (quoted strings)::.
15.5.4 Exercise: named rooms
----------------------------
!!!!! And another: implement the Crowther and Woods feature of moving
from one room to another by typing its name, discussed above, using a
dummy verb and without using `Replace' to change any library routines.
*Note Answer (named rooms)::.
16 Scope and what you can see
*****************************
He cannot see beyond his own nose. Even the fingers he
outstretches from it to the world are (as I shall suggest) often
invisible to him.
- Max Beerbohm (1872-1956), of George Bernard Shaw
Wherefore are these things hid?
-- William Shakespeare (1564--1616), `Twelfth Night'
16.1 What `in scope' means
==========================
Time to say what `in scope' means. This definition is one of the most
important algorithms in the game, although it is buried inside the
parser, because it decides what the player is allowed to refer to: so
here it is in full. The following are in scope:
the player's immediate possessions;
the 12 compass directions;
*if* there is light, the objects in the same place as the player.
In addition, if an object is in scope then its immediate possessions are
in scope, *if* it is `see-through', which means that:
the object has `supporter', *or*
the object has `transparent', *or*
the object is an `open' `container'.
The definition of when there is light will be gone into in the next
section.
!!!! Many things are still in scope in a dark room - so the player can
still turn a lamp on if it's being carried. On the other hand, a
player who puts the lamp on the ground and turns it off then loses the
ability to turn it back on again, because it is out of scope. (This
can be changed; *Note Changing scope::.)
!!!! The compass direction objects make sense as objects. The player
can always type something like
`attack the south wall'
(and a `before' rule for the room in question could make something
unusual happen as a result). The compass directions are visible in the
dark, as otherwise the player could not still walk around.
!!!! The parser applies scope rules to other people too, not just the
player. Thus `dwarf, drop sword' will be accepted even if the sword is
invisible to the player provided it is visible to the dwarf.
!!!! Items with the `concealed' attribute may be concealed from
ordinary descriptions - but the parser still sees them: they are still
in scope. (If you want things to be both concealed and unreferrable-to,
put them somewhere else!)
!!!!! Actually, the above definition is not quite right, because the
compass directions are not in scope when the player asks for a plural
number of things, like `take all the knives'. (This makes some of the
parser's plural algorithms run faster.) Also, for a `multiexcept'
token, the other object is not in scope; and for a `multiinside' token,
only objects in the other object are in scope. This makes `take
everything from the cupboard' work in the natural way.
16.2 Changing the scope rules
=============================
The rest of this section is about how to change the scope rules. As
usual with Inform, you can change them globally, but it's more efficient
and safer to do so only locally.
A typical example: how do we allow the player to ask questions like the
traditional `what is a grue'? The `grue' part ought to be parsed as if
it were a noun, so that we could distinguish between, say, a `garden
grue' and a `wild grue'. So it isn't good enough to look only at a
single word. Here is one solution:
Object questions "qs";
[ QuerySub;
print_paddr noun.description; new_line;
];
[ Topic i;
if (scope_stage==1) rfalse;
if (scope_stage==2) {
objectloop (i in questions) PlaceInScope(i);
rtrue;
}
"At the moment, even the simplest questions confuse you.";
];
where the actual questions are themselves objects which belong to the
`questions' object, like so:
Object q1 "long count" questions
with name "long" "count",
description "The Long Count is the great Mayan cycle of time, \
which began in 3114 BC and will finish with the world's \
end in 2012 AD.";
and we also have a grammar line:
Verb "what"
* "is" scope=Topic -> Query
* "was" scope=Topic -> Query;
(Note that the `questions' and `q1' objects are out of the game for
every other purpose. The name `qs' doesn't matter; the individual
questions are named so that the parser might be able to say `Which do
you mean, the long count or the short count?' if the player just asked
`what is the count'.) Here's what happens: when the parser reaches
`scope=Topic', it calls the `Topic' routine with the variable
`scope_stage' set to 1. The routine should return 1 (true) if it is
prepared to allow multiple objects to be accepted here, and 0 (false)
otherwise: as we don't want `what is everything' to list all the
questions and answers in the game, we return false.
A little later on in its machinations, the parser again calls `Topic'
with `scope_stage' now set to 2. `Topic' is now under an obligation to
tell the parser which objects are to be in scope. It can call two
parser routines to do this.
ScopeWithin(object)
puts everything inside the object into scope, though not the object
itself;
PlaceInScope(object)
puts just a single object into scope. It is perfectly legal to declare
something in scope that `would have been in scope anyway': or even
something which is in a different room altogether from the actor
concerned, say at the other end of a telephone line. Our scope routine
`Topic' should then return
`0 (false)'
to carry on with the usual scope rules, so that everything that
would usually be in scope still is, and
`1 (true)'
to tell the parser not to put any more objects into scope.
(So at `scope_stage' 2 it is quite permissible to do nothing but return
false, whereupon the usual rules apply.) `Topic' returns true because
it wants only question topics to be in scope, not question topics
together with everything near the player.
If the player had typed `what is the long count', that would be all
that happens and all would be well. On the other hand, if the player
typed `what is the lgon cnout' owing to a bout of dyslexia, the error
message which the parser would usually produce (`You can't see any such
thing') would be unsatisfactory. So if parsing failed at this token,
then `Topic' is called at `scope_stage' 3 to print out a suitable error
message. (It must provide one.)
!!!! The `scope=' notation is available only under Inform 5.4 and later.
!!!! Note that `ScopeWithin(object)' extends the scope down through its
possessions according to the usual rules (i.e., depending on their
transparency, whether they're containers and so in). The definition of
`Topic' above shows how to put just the direct possessions into scope.
16.2.1 Exercise: purloin verb
-----------------------------
Write a token which puts everything in scope, so that you could have a
debugging `purloin' verb which could take anything (regardless of where
it was and the rules applying to it).
*Note Answer (purloin verb)::.
16.3 Changing the global scope rules
====================================
Changing the global definition of scope should be done cautiously (there
may be unanticipated side effects); bear in mind that scope decisions
need to be taken often - every time an object token is parsed, so
perhaps five to ten times in every game turn - and hence moderately
quickly.
To do this one provides a routine called
InScope(actor)
where the `actor' is the person whom the parser is working out scope
for; usually, then, `actor=player', but this should not be assumed to
be always the case. If the routine decides that a particular object
should be in scope for the actor, it should execute `InScope' and
`ScopeWithin' just as above, and return true or false (as if it were at
`scope_stage' 2). Thus, it is vital to return false in circumstances
when you don't want to intervene.
!!!! In the case of a token `scope='ROUTINE, the ROUTINE gets first
crack at scope-changing, and `InScope' will only be reached if it
returns false to signify `carry on'.
Here are some examples. Firstly, as promised above, how to change the
rule that `things you've just dropped disappear in the dark':
[ InScope person i;
if (person==player && location==thedark)
objectloop (i near player)
if (i has moved)
PlaceInScope(i);
rfalse;
];
With this routine added, the objects in the dark room the player is in
are in scope only if they have `moved' (that is, have been held by the
player in the past); and even then, are in scope only to the player.
!!!! By combining an `InScope' routine with token-parsing routines in
grammar, dramatic effects are possible. See the example game
`Balances', for instance.
16.3.1 Exercise: light switch
-----------------------------
!!!!! Code the following puzzle, which has nasty scope complications.
In an initially dark room there is a light switch. Provided you've seen
the switch at some time in the past, you can turn it on and off - but
before you've ever seen it, you can't. Inside the room is nothing you
can see, but you can hear a dwarf breathing. If you tell the dwarf to
turn the light on, he will.
*Note Answer (light switch)::.
16.4 Parser error messages
==========================
!!!!! Once you begin programming the parser on a large scale, you soon
reach the point where the parser's ordinary error messages no longer
appear sensible. So that you can change the rules even at this last
hurdle, the parser calls your `ParserError' routine (if you provide
one): the argument is the error type, and you return true to tell the
parser to shut up (as you have already printed a better error message
than it would have done). The error types are all defined as constants:
`STUCK_PE'
I didn't understand that sentence.
`UPTO_PE'
I only understood you as far as...
`CANTSEE_PE'
You can't see any such thing.
`TOOLIT_PE'
You seem to have said too little!
`NOTHELD_PE'
You aren't holding that!
`MULTI_PE'
You can't use multiple objects with that verb.
`MMULTI_PE'
You can only use multiple objects once on a line.
`VAGUE_PE'
I'm not sure what `it' refers to.
`EXCEPT_PE'
You excepted something not included anyway!
`ANIMA_PE'
You can only do that to something animate.
`VERB_PE'
That's not a verb I recognise.
`SCENERY_PE'
That's not something you need to refer to...
`ITGONE_PE'
You can't see ~it~ (the spell book) at the moment.
`JUNKAFTER_PE'
I didn't understand the way that finished.
`TOOFEW_PE'
Only five of those are available.
`NOTHING_PE'
Nothing to do!
`NUMBER_PE'
I didn't understand that number.
`ASKSCOPE_PE'
*whatever the scope routine prints*
Each time the parser gives up on a line of grammar, it has hit one of
these conditions. A verb may have many lines of grammar; so by the time
the parser wants to print an error, all of them must have failed. The
error message it prints is the most `interesting' one (meaning, lowest
down this list).
17 The light and the dark
*************************
The library maintains light by itself, and copes with events like:
a total eclipse of the Sun;
fusing all the lights in the house;
your lamp going out;
a dwarf stealing it and running away;
dropping a lit match which you were seeing by;
putting your lamp into an opaque box and shutting the lid;
black smoke filling up the glass jar that the lamp is in;
the dwarf with your lamp running back into your now-dark room.
The point of this list is to demonstrate that light versus darkness is
tricky to get right, and that it is best left to the library. Your code
needs only to do something like
give lamp light;
remove match;
give glass_jar ~transparent;
move dwarf to Dark_Room;
and can leave the library to sort out the consequences. As the above
suggests, the `light' attribute means that an object is giving off
light, or that a room is currently lit (e.g., by being out of doors in
day-time).
If you simply never want to have darkness, and some games never do, a
sneaky way of doing it is to put the line
give player light;
in `Initialise'. The game works as if the player herself were glowing
enough to provide light to see by. So there's never darkness near the
player.
The definition of `when there is light' is complicated, involving
recursion both up and down. Remember that the parent of the player
object may not be a room; it may be, say, a red car whose parent is a
room.
There is light exactly when the parent of the player `offers light'. An
object `offers light' if:
it itself has the `light' attribute set, *or*
any of its immediate possessions `have light', *or*
it is see-through and its parent offers light, *or*
it is enterable and its parent offers light;
while an object `has light' if:
it currently has the `light' attribute set, *or*
it is see-through and one of its immediate possessions has light.
The process of checking this stops as soon as light is discovered. The
routines
`OffersLight(object)' and `HasLightSource(object)'
which return true or false and have no side effects (that is, they
merely report on the current state without changing anything), are
available and might occasionally be useful.
!!!! So light is cast up and down the tree of objects. In certain
contrived circumstances this might be troublesome: perhaps an opaque
box, whose outside is fluorescent but whose interior is dark, and which
contains an actor who needs not to have other contents of the box in
scope... `contrived' being the word. The dilemma could be solved by
putting an inner box in the outer one.
Each turn, light is reconsidered. The presence or absence of light
affects the `Look', `Search', `LookUnder' and `Examine' actions, and
(since this is a common puzzle) also the `Go' action: you can provide a
routine called
DarkToDark()
and if you do then it will be called when the player goes from one dark
room into another dark one. If you want, you can take the opportunity
to kill the player off or extract some other forfeit. If you provide no
such routine, then the player can move about freely (subject to any
rules which apply in the places concerned).
17.0.1 Exercise: heliotropic troll
----------------------------------
How would you code a troll who is afraid of the dark, and needs to be
bribed but will only accept a light source... so that the troll will be
as happy with a goldfish bowl containing a fluorescent jellyfish as he
would be with a lamp?
*Note Answer (heliotropic troll)::.
18 On inventories and lists
***************************
As some day it may happen that a victim must be found
I've got a little list -- I've got a little list
Of society offenders who might well be underground,
And who never would be missed
Who never would be missed!
-- W. S. Gilbert (1836--1911), `The Mikado'
18.1 Producing lists
====================
The library often needs to reel off a list of objects: when an `Inv'
(inventory) action takes place, for instance, or when describing the
contents of a container or a room. Lists are difficult to print out
correctly `by hand', because there are many cases to get right
(especially when taking plurals into account). Fortunately, the
library's list-maker is open to the public. The routine to call is:
WriteListFrom(object, style);
where the list will start from the given object and go along its
siblings. (Thus, to list all the objects inside `X', list from
`child(X)'.) What the list looks like depends on the style, which is a
bitmap you can make by adding up some of the following constants:
`NEWLINE_BIT'
New-line after each entry
`INDENT_BIT'
Indent each entry by depth
`FULLINV_BIT'
Full inventory information after entry
`ENGLISH_BIT'
English sentence style, with commas and `and'
`RECURSE_BIT'
Recurse downwards with usual rules
`ALWAYS_BIT'
Always recurse downwards
`TERSE_BIT'
More terse English style
`PARTINV_BIT'
Only brief inventory information after entry
`DEFART_BIT'
Use the definite article in list
`WORKFLAG_BIT'
At top level (only), only list objects which have the `workflag'
attribute
The best way to use this is to experiment. For example, a `tall'
inventory is produced by:
WriteListFrom( child(player),
FULLINV_BIT + INDENT_BIT + NEWLINE_BIT + RECURSE_BIT );
and a `wide' one by:
WriteListFrom( child(player),
FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT );
which produce effects like:
>inventory tall
You are carrying:
a bag (which is open)
three gold coins
two silver coins
a bronze coin
four featureless white cubes
a magic burin
a spell book
>inventory wide
You are carrying a bag (which is open), inside which are three gold
coins, two silver coins and a bronze coin, four featureless white
cubes, a magic burin and a spell book.
(except that the `You are carrying' part is not done by the list-maker,
and nor is the final full stop in the second example).
The `workflag' is an attribute which the library scribbles over from
time to time as temporary storage, but you can use it with care. In
this case it makes it possible to specify any reasonable list.
18.1.1 Exercise: double inventory
---------------------------------
Write a `DoubleInvSub' action routine to produce an inventory like so:
You are carrying four featureless white cubes, a magic burin and a
spell book. In addition, you are wearing a purple cloak and a
miner's helmet.
*Note Answer (double inventory)::.
18.2 Changing an object's description in a list
===============================================
Some objects ought to print something different when they appear in
inventory lists: a wine bottle should say how much wine is left, for
instance. By giving an object an `invent' routine, you can alter the
usual rules.
A typical inventory list looks like:
>i
You are carrying:
a green cone
a pair of hiking boots
your satchel (which is open)
a green cube
and each line goes through two stages. Firstly, the object's indefinite
article and name are printed. Secondly, little informative messages
like `(which is open)' are printed, and inventories are given for the
contents of open containers.
An object's `invent' routine gets two chances to interfere with this,
one before each stage. So what happens is this:
1. The global variable `inventory_stage' is set to 1.
2. The `invent' routine is called (if there is one). If it returns
true, stop here.
3. The indefinite article and short name are printed.
4. The global variable `inventory_stage' is set to 2.
5. The `invent' routine is called (if there is one). If it returns
true, go to step 8.
6. One or more of the following are printed, as appropriate:
(providing light)
(being worn)
(which is open)
(which is closed)
7. If it is an open container, its contents are inventoried.
8. Linking text (such as a new-line or a comma) is printed, according
to the current style.
Note that if an `invent' routine does return true, then the contents
are never reached.
For example, here is an `invent' routine for a matchbook:
invent [ i;
if (inventory_stage==2) {
i=self.number;
if (i==0) print " (empty)";
if (i==1) print " (1 match left)";
if (i>1) print " (",i," matches left)";
}
],
(The rest of the matchbook definition is given in the `Toyshop' example
game.)
!!!!! Changing the rules at `inventory_stage' 1 is kept on for more
peculiar effects (and is not really recommended).
19 The naming of names
**********************
The Naming of Cats is a difficult matter,
It isn't just one of your holiday games;
You may think at first I'm as mad as a hatter
When I tell you, a cat must have THREE DIFFERENT NAMES.
-- T. S. Eliot (1888--1965), `The Naming of Cats'
Bulldust, coolamon, dashiki, fizgig, grungy, jirble, pachinko,
poodle-faker, sharny, taghairm
- Catachrestic words from Chambers English Dictionary
Providing an `invent' routine doesn't work quite so well for the match
as for the matchbook. You might try:
invent [;
if (inventory_stage==2) {
if (self has light)
print " (burning away)";
rtrue;
}
],
!!!! Returning true here is a trick to stop `(providing light)' from
appearing as well - after all, that's redundant.
This does work but there's a better way, which is to change the object's
short name itself, from `unlit match' to `lit match' say. So another
property, called `short_name', is available. Normally, the short name
of an object is given in the header of the object definition. But if a
`short_name' routine is given, then whenever the game wants to write
out the name of the object, the following happens:
1. If the `short_name' is a string, it's printed and that's all.
2. If it is a routine, then it is called. If it returns true, that's
all.
3. The `real' short name (the one in the object header) is printed.
So, as promised, here's the better way to code the match:
short_name [;
if (self has light) print "burning match";
else print "unlit match";
rtrue;
],
Note that rooms can also be given a `short_name' routine, which might
be useful to code, say, a grid of four hundred almost exactly similar
locations, called `Area 1' up to `Area 400'.
!!!! Now, though, the player is going to want to type `drop the burning
match", or `light the unlit match'. (Indeed, there might be two
matches, only one of which can be called `unlit'.) It's perfectly
straightforward to alter the `name' property of an object (provided one
remembers what it is: a list of (machine) words, being the byte
addresses in memory of words in the dictionary) but, again, there is a
better and more powerful way. This is to give the match object a
`parse_name' routine.
A `parse_name' routine is expected to try to match the text which the
user has typed (available one word at a time from the `NextWord()'
routine), and to return how many words it managed to match. It is
required to go as far as it can, i.e., not to stop if the first word
makes sense, but to keep reading and find out how many words in a row
make sense.
It should return:
`0'
if the text didn't make any sense at all,
`k'
if k words in a row of the text seem to refer to the object, or
`-1'
to make the parser just do what it would normally do.
For example:
Nearby thing "weird thing"
with parse_name [ i;
while (NextWord()=='weird' or 'thing') i++;
return i;
];
This definition duplicates (very nearly) what the parser would have done
if the weird thing had been defined as:
Nearby thing "weird thing"
with name "weird" "thing";
Which isn't very useful. But the match can now be coded up with
parse_name [ i j;
if (self has light) j='burning';
else j='unlit';
while (NextWord()=='match' or j) i++;
return i;
],
so that `burning' only applies when it is, and similarly `unlit'.
(Actually the parser automatically recognises `unlit' and `lit', so this
last part was unnecessary.)
20 Plural names and groups of similar objects
*********************************************
Abiit ad plures.
-- Petronius (?--c. 66), `Cena Trimalchionis'
A notorious problem for adventure game parsers is to handle a collection
of, say, ten gold coins, allowing the player to use them independently
of each other, while gathering them together into groups in descriptions
and inventories. This is relatively easy in Inform, and only in really
hard cases do you have to provide code.
There are two problems to be overcome: firstly, the game has to be able
to talk to the player in plurals, and secondly vice versa. First, then,
game to player:
Class gold_coin_class
with name "gold" "coin",
plural "gold coins";
(and similar silver and bronze coin classes here)
Object bag "bag"
with name "bag"
has container open openable;
Nearby co1 "gold coin" class gold_coin_class;
Nearby co2 "gold coin" class gold_coin_class;
Nearby co3 "gold coin" class gold_coin_class;
Nearby co4 "silver coin" class silver_coin_class;
Nearby co5 "silver coin" class silver_coin_class;
Nearby co6 "bronze coin" class bronze_coin_class;
Now we have a bag of six coins. The player looking inside the bag will
get
>look inside bag
In the bag are three gold coins, two silver coins and a bronze coin.
How does the library know that the three gold coins are the same as each
other, but the others different? It doesn't look at the classes but the
names. It will only group together things which:
1. have a `plural' set,
2. are `indistinguishable' from each other.
Indistinguishable means they have the same `name' words as each other
(possibly in a different order), so that nothing the player can type
will separate the two.
!!!! Actually, the library is cleverer than this. What it groups
together depends slightly on the context of the list it's writing out.
When it's writing a list which prints out details of which objects are
providing light, for instance (like an inventory), it won't group
together two objects if one is lit and the other isn't. Similarly for
objects with visible possessions or which can be worn.
!!!!! This all gets even more complicated when the objects have a
`parse_name' routine supplied, because then the library can't use the
`name' fields to tell them apart. If they have different `parse_name'
routines, it decides that they're different. But if they have the same
`parse_name' routine, there is no alternative but to ask them. What
happens is that
1. A variable called `parser_action' is set to `##TheSame';
2. Two variables, called `parser_one' and `parser_two' are set to the
two objects in question;
3. Their `parse_name' routine is called. If it returns:
`-1'
the objects are declared `indistinguishable',
`-2'
they are declared different.
4. Otherwise, the usual rules apply and the library looks at the
ordinary `name' fields of the objects.
(`##TheSame' is a fake action.) The implementation of the `Spellbreaker
cubes' in the `Balances' game is an example of such a routine (so that
if the player writes the same name on several of the cubes, they become
grouped together). Note that this whole set-up is such that if the
author of the `parse_name' routine has never read this paragraph, it
doesn't matter and the usual rules take their course.
!!!!! You may even want to provide a `parse_name' routine just to speed
up the process of telling two objects apart - if there were 30 gold
coins the parser will be doing a lot of work comparing all their names,
but you can make the decision much faster.
Secondly, the player talking to the computer. This goes a little
further than just copies of the same object: many games involve
collecting a number of similar items, say a set of nine crowns in
different colours. Then you'd want the parser to recognise things like:
> drop all of the crowns except green
> drop the three other crowns
even though the crowns are not identical. The simple way to do this is
just to put `"crowns"' in their name fields, and this works perfectly
well most of the time.
!!!!! But it isn't ideal, because then the parser will think
> drop crowns
refers to a single object, and won't deduce that the player wants to
pick up all the sensibly available crowns. So the complicated (but
better way) is to make the `parse_name' routine tell the parser that
yes, there was a match, but that it was a plural. The way to do this
is to set `parser_action' to `##PluralFound' (another fake action).
So, for example:
Class crown_class
with parse_name [ i j;
for (::) {
j=NextWord();
if (j=='crown' or j==self.name) i++;
else {
if (j=='crowns') {
parser_action=##PluralFound;
i++;
}
else return i;
}
}
];
20.0.1 Exercise: cherubim
-------------------------
Write a `cherub' class so that if the player tries to call them
`cherubs', a message like `I'll let this go by for now, but the plural
of cherub is cherubim" appears.
*Note Answer (cherubim)::.
21 Miscellaneous constants and scoring
**************************************
Some game rules can be altered by defining `constants' at the start of
the program, before the library files are included. Two constants it
*must* provide are the strings `Story' and `Headline':
Constant Story "ZORK II";
Constant Headline "^An Interactive Plagiarism^\
Copyright (c) 1993 by Ivan O. Ideas.^";
All the rest are optional.
21.1 Limiting the objects carried by the player
===============================================
The library won't allow the player to carry an indefinite number of
objects: the limit allowed is the constant `MAX_CARRIED', which you may
define if you wish. If you don't define it, it's 100, which roughly
removes the rule. (In fact you can change this `live', in that it is
actually `player.capacity' which is consulted; the only use of
`MAX_CARRIED' is to set this up to an initial value.)
If you define `SACK_OBJECT' to be some container, then the player will
automatically put old, least-used objects away in it as the game
progresses, provided it is being carried. This is a feature which
endears the designer greatly to players.
21.2 Providing closing credits
==============================
Another constant is `AMUSING_PROVIDED'. If you define this, the
library knows to put an `amusing' option on the menu after the game is
won. It will then call `Amusing()' from your code when needed. You
can use this to roll closing credits, or tell the player various
strange things about the game, now that there's no surprise left to
spoil.
21.3 The scoring system
=======================
The other constants you are allowed to define help the score routines
along. There are two scoring systems provided by the library, side by
side: you can use both or neither. (You can always do what you like to
the `score' variable in any case.) One scores points for getting
certain items or reaching certain places; the other for completing
certain actions. The constants are:
`MAX_SCORE'
The maximum game score (by default 0)
`NUMBER_TASKS'
Number of individual `tasks' to perform (1)
`OBJECT_SCORE'
Bonus for first picking up a `scored' object (4)
`ROOM_SCORE'
Bonus for first entering a `scored' room (5)
and then the individual tasks have scores, as follows:
Global task_scores initial t1 t2 ... tn;
Within your code, when a player achieves something, call
`Achieved(task)' to mark that the task has been completed. It will
only award points if this task has not been completed before.
There do not have to be any `tasks': there's no need to use the scoring
system provided. Tasks (and the verb `full' for full score) will only
work at all if you define the constant `TASKS_PROVIDED'.
A routine called `PrintRank', which you can provide if you want, gets
the chance to print something additional to the score, such as
rankings. For instance:
[ PrintRank;
print ", earning you the rank of ";
if (score >= 348) "Grandmaster Adventurer!";
if (score >= 330) "Master, first class.";
if (score >= 300) "Master, second class.";
if (score >= 200) "Junior Master.";
if (score >= 130) "Seasoned Adventurer.";
if (score >= 100) "Experienced Adventurer.";
if (score >= 35) "Adventurer.";
if (score >= 10) "Novice.";
"Amateur.";
];
`PrintTaskName' prints the name of a game task (such as `driving the
car'). Of course, this is only ever called in a game with
`TASKS_PROVIDED' defined. For instance,
[ PrintTaskName ach;
if (ach==0) "eating a sweet";
if (ach==1) "driving the car";
if (ach==2) "shutting out the draught";
if (ach==3) "building a tower of four";
if (ach==4) "seeing which way the mantelpiece leans";
];
Normally, an Inform game will print messages like
[Your score has gone up by three points.]
when the score changes (by whatever means). The player can turn this on
and off with the `notify' verb; by default it is on. You can alter the
flag `notify_mode' yourself to control this.
21.4 Asking `yes or no' questions of the player
===============================================
Sometimes you want to ask the player a yes/no question. If so, call
`YesOrNo()', a library routine: it returns true/false accordingly (and
doesn't print any question, which is up to you). This saves a lot of
bother programming the parser.
21.5 Replacing library routines
===============================
!!!!! Occasionally you need to rewrite one of the library routines.
But the danger of doing so is that it is then necessary to keep a copy
of the library for every game, which is clearly unsatisfactory. So: for
example, if the directive
REPLACE BurnSub;
is placed in your file *before the library files are included*, Inform
ignores the definition of `BurnSub' in the library files. (You then
have to define a routine called `BurnSub' yourself, or Inform will
complain that the program refers to a routine which isn't there.) And
this is the very last resort, and so the end of the manual proper.
22 Debugging verbs and tracing
******************************
If builders built buildings the way programmers write programs, the
first woodpecker that came along would destroy civilisation.
- old computing adage
22.1 Debugging verbs
====================
The two problems with debugging a game are working out exactly what it's
doing, and making things happen which wouldn't normally be allowed (such
as giving yourself the trident now rather than playing for two hours to
find it).
Inform provides a small suite of debugging verbs to help with this, but
only if the game defines the constant `DEBUG' before including the
library files. (Just in case you forget to take this out again, the
letter D appears in the game banner to indicate this.)
You then get the following verbs, which can be used at any time in play:
purloin ANYTHING
abstract ANYTHING to ANYTHING
goto NUMBER
tree tree THING
actions actions on actions off
routines routines on routines off
timers timers on timers off
trace trace on trace off trace 1 TO 5
You can `purloin' any item or items in your game at any time, wherever
you are. You can likewise `abstract' any item to any other item
(meaning: move it to the other item). To get a listing of the objects
in the game and how they contain each other, use `tree', and to see the
possessions of one of them alone, use `tree THAT'. Finally, you can go
to any object: but since rooms don't have names understood by the
parser, you have to give the object number of the place you want to go
to. (The `tree' listing will tell you these numbers.)
Turning on `actions' gives a trace of all the actions which take place
in the game (the parser's, the library's or yours); turning on
`routines' traces every object routine (such as `before' or `life')
that is ever called, except for `short_name' (as this would look
chaotic, especially on the status line). Turning on `timers' shows the
state of all active timers and daemons each turn.
22.2 `Infix', Inform's debugger
===============================
Infix, Dilip Sequeira's source-level debugger for Inform, is currently
in an advanced state of preparation: it is an enhanced form of Mark
Howell's Zip interpreter providing for breakpoints, tracing and so
forth. It should fairly soon be publically archived with the rest of
the Inform project.
!!!! For Infix's benefit, Inform (if compiling with the option set)
produces a file of `debugging information' (cross-references of the game
file with the source code), and anyone interested in writing an Inform
utility program may want to know the format of this file: see the short
C program Infact which prints out the debugging information file in
English.
22.3 How to crash an interpreter
================================
On most interpreters, though, run-time crashes are mysterious (because
they were written on the assumption that all existing Infocom game files
were free from error). Zip is rather more generous and will usually
tell you why and where the problem is; given a game file address you can
work back to the problem point in the source either with Mark Howell's
txd (disassembler) or by running Inform with the assembler trace option
on.
Here are all the ways I know to crash an interpreter at run-time (with
high-level Inform code, that is; if you insist on using assembly
language or the `indirect' function you're raising the stakes),
arranged in decreasing order of likelihood:
* Writing to a property of an object which it hasn't got;
* Dividing by zero, possibly by calling `random(0)';
* Giving a string or numerical value for a property which can only
legally hold a routine, such as `before', `after' or `life';
* Applying `parent', `child' or `children' to the `nothing' object;
* Using `print object' on the `nothing' object, or for some object
which doesn't exist (always use the routines `DefArt', `CDefArt'
or `PrintShortName' as appropriate, which are safeguarded and
anyway implement higher-level features);
* Using `print_addr' or `print_paddr' to print from an address
outside the memory map of the game file, or an address at which no
string is present (this will result in random text appearing,
possibly including unprintable characters, which may conceivably
crash your terminal);
* Setting the `location' variable to zero, or some non-existent
object, since the interpreter prints the name of this object on the
status line; in any case it is safer to use `PlayerTo' than to
meddle directly with this;
* Running out of stack space in a recursive loop (though this has
never actually happened to anyone I know of).
22.4 Tracing the parser
=======================
!!!! There are times when it's hard to work out what the parser is up to
and why (actually, most times are like this). The parser is written in
levels, the lower levels of which are murky indeed. Most of the
interesting things happen in the middle levels, and these are the ones
for which tracing is available. The levels which can be traced are:
Level 1
Grammar lines
Level 2
Individual tokens
Level 3
Object list parsing
Level 4
Resolving ambiguities and making choices of object(s)
Level 5
Comparing text against an individual object
`trace' or `trace on' give only level 1 tracing. Be warned: `trace
five' can produce reams of text when you try anything at all
complicated: but you do sometimes want to see it, to get a list of
exactly everything that is in scope and when. There are two levels
lower than that but they're too busy doing dull spade-work to waste time
on looking at `parser_trace'. (There's also a level 0, but it consists
mostly of making arrangements for level 1, and isn't very interesting.)
!!!!! Finally, though this is a drastic measure, you can always compile
your game `-g' (`debugging code') which gives a listing of every
routine ever called and their parameters. This produces an enormous
melée of output. (In Inform 5.3, though not earlier versions, you can
declare a routine with an asterisk `*' as its first local variable,
which produces such tracing only for that one routine.)
23 Limitations on the run-time format
*************************************
How wide the limits stand
Between a splendid and an happy land.
-- Oliver Goldsmith (1728--1774), `The Deserted Village'
The Infocom run-time format is well-designed, and has three major
advantages: it is compact, widely portable and can be quickly executed.
Nevertheless, like any rigidly defined format (which it clearly must
be), it imposes limitations. These are not by any means pressing.
Inform itself has a flexible enough memory-management system not to
impose artificial limits on numbers of objects and the like.
There are two sizes of game available: Standard (or version 3) and
Advanced (or version 5). Always use the latter if you can; it is very
much to be preferred. (There are very slight risks associated with
exotic assembly-language coding in Advanced games: but unless you
deviate far from normal Inform practice, and code on a very low level,
these will never trouble you.) There are still 8-bit computers in use
which are too small for Advanced games, but most people should have no
problem.
*Memory*
The total size of a Standard game is at most 128K; of an Advanced
game, 256K. Because games are encoded in a very compressed form,
and because the centralised library of Inform makes the compiler
fairly efficient in terms of not duplicating code, even 128K
allows for a game at least half as large again as a typical
old-style Infocom game. No game yet written under Inform has
reached the 256K mark, not even the final version of `Curses' (at
224K, already substantially bigger and denser than any Infocom
game).
*Vocabulary*
There is no theoretical limit, and vocabularies in excess of 2000
are quite sensibly possible. (A typical large game will muster
1000.)
*Dictionary resolution*
In Standard games, dictionary words are truncated to their first 6
letters, which can be a nuisance; in Advanced, 9 letters, which
never is.
*Attributes, properties, names*
Standard games only provide 32 attributes, 30 properties and at
most 8 bytes of data per property: the library consumes most of
this ration, and the 8 bytes restriction means that an object can
only have four words in its `name'. This is perfectly manageable,
if unsatisfactory. Advanced games, which provide 48 attributes, 62
properties and 64 bytes of data (hence 32 names), effectively make
none of these restrictions serious.
*Special effects*
Standard games cannot have special effects such as bold face and
underlining. *Note Styles::.
*Objects*
Standard games can have at most 255 objects, enough for a large
Infocom-size game but not much more. Advanced games have no such
restriction, the number being in principle unlimited. Objects
cannot dynamically be created or destroyed, but this is imitated
easily enough.
*Global variables*
There can only be 240 of these, and the Inform compiler uses 5 as
scratch space, while the library uses slightly over 100; but since
a typical game uses only a dozen of its own, code being almost
always object-oriented, the restriction is never even nearly felt.
(Recall that arrays take only one global each.)
*`Undo'*
Standard games do not provide an `undo' verb, but Advanced ones (or
rather, the Inform library when compiled on such) do.
!!!! There is a yet larger Infocom format, version 6, which lifts even
the mild restrictions of version 5 games, with memory maps of up to 600K
or so: version 6 has only recently been fully deciphered, but Inform is
already capable of producing such files, and Mark Howell's `Zip'
interpreter is (at time of writing) making good progress in running
them. A 600K Inform game would be gargantuan: by the time one comes
along, there should be no problem in running it.
!!!!! Having said all this, if memory does become short, there is a
standard mechanism for saving about 8-10% of the memory. Inform does
not usually trouble to, since there's very seldom the need, and it makes
the compiler run about 10% slower. What you need to do is define
abbreviations. For instance, the directive
Abbreviate " the ";
(placed before any text appears) will cause the string ` the ' to be
internally stored as a single `letter', saving memory every time it
occurs (about 2500 times in `Curses', for instance). You can have up to
64 abbreviations. A good list of abbreviations can be found in the
`Technical Manual': basically, avoid proper nouns and instead pick on
short combinations of a space and common two- or three-letter blocks.
You can even get Inform to work out by itself what a good stock of
abbreviations would be: but be warned, this makes the compiler run about
29000% slower.
24 A very few, not very special effects
***************************************
Yes, all right, I won't do the menu... I don't think you realise
how long it takes to do the menu, but no, it doesn't matter, I'll
hang the picture now. If the menus are late for lunch it doesn't
matter, the guests can all come and look at the picture till they
are ready, right?
- John Cleese and Connie Booth, `Fawlty Towers'
As the previous section suggested, some special effects are not to be
encouraged: they reduce portability a little, and anyway Inform is for
text games.
24.1 The status line
====================
The status line is perhaps the most distinctive feature of Infocom games
in play. This is the (usually highlighted) bar across the top of the
screen. Usually, the game automatically prints the current game
location, and either the time or the score and number of turns taken.
It has the score/turns format unless the directive
Statusline time;
is present, in which case it shows the game's 24-hour clock.
!!!!! If you *really* want to change this, then `Replace' the parser's
private `DrawStatusLine' routine. But be sure to test the result on
more than one interpreter, in case you've made a mistake which doesn't
show up on one quirky implementation. (In any case, you can only
change it at all on Advanced games.)
24.1.1 Exercise: status line
----------------------------
Alter the `Advent' example game to display the number of treasures
found instead of the score and turns on the status line.
*Note Answer (status line)::.
24.2 Character graphics
=======================
About character graphic drawings: on some machines, text will by default
be displayed in a proportional font (i.e., one in which the width of a
letter depends on what it is, so that for example an `i' will be
narrower than an `m'). If you want to display a diagram made up of
letters, such as a map, you will have to turn this off, for which the
`font' command is provided. Write `font off' before printing character
graphics, and remember to write `font on' again afterwards.
*WARNING:* Don't turn the `font' on and off in the middle of a line;
this doesn't look right on some machines.
24.3 Putting quotes in boxes
============================
A distinctive feature of later Infocom games was their use of epigrams.
Inform can do this, too, but only for Advanced games. The assembly
language required is easy but a nuisance, so there is a command to do
it, `box'. For example,
box "Beware of the Dog";
or
box "I might repeat to myself, slowly and soothingly,"
"a list of displays beautiful from minds profound;"
"if I can remember any of the damn things."
""
"-- Dorothy Parker";
Note that a string of lines is given (without intervening commas) and
that a blank line is given by a null string. Remember that the text
cannot be too wide or it will look awful on a small screen.
The author takes the view that this device is amusing for irrelevant
displays but irritating when it conveys vital information (such as,
`Beware of the Dog', for instance). Also, some people might be running
your game on a laptop with a vertically challenged screen, so it is
polite to provide a `quotes off' verb.
24.4 Interactive menus
======================
Sometimes one would like to provide a menu of text options (for
instance, when producing instructions which have several topics, or when
giving clues). This can be done with the `DoMenu' routine, which
imitates the traditional `Invisiclues' style. However, it only looks
good for Advanced games; for Standard games, it's simply textual. (In
an Advanced game, by setting `pretty_flag=0' you can get this simple
text version instead; which is a good idea for machines with very small
screens.)
Here is a typical call to `DoMenu':
DoMenu("There is information provided on the following:^\
^ Instructions for playing\
^ The history of this game\
^ Credits^",
#r$HelpMenu, #r$HelpInfo);
Note the layout, and especially the carriage returns. The second and
third arguments are themselves routines: the notation `#r$', seldom
seen in high-level Inform, allows routine names to become ordinary
numerical values. The first routine, in this case called `HelpMenu',
is supposed to look at the variable `menu_item'. In the case when this
is zero, it should return the number of entries in the menu (3 in the
example). In any case it should set `item_name' to the title for the
page of information for that item; and `item_width' to half its length
in characters (this is used to centre titles on the screen: it is
unfortunately tricky for the game to compute the length of a static
string, so you have to tell it). In the case of item 0, the title
should be that for the whole menu.
The second routine, `HelpInfo' above, should simply look at `menu_item'
(1 to 3 above) and print the text for that selection. After this
returns, normally the game prints `Press [Space] to return to menu' but
if the value 2 is returned it doesn't wait.
Menu items can safely launch whole new menus, and it is easy to make a
tree of these (which will be needed when it comes to providing hints
across any size of game).
24.4.1 Exercise: invisiclues
----------------------------
Code an `Invisiclues'-style sequence of hints for a puzzle, revealed one
at a time, as a menu item.
*Note Answer (invisiclues)::.
24.5 Text styles
================
Finally, though it should only be used sparingly (and again is
restricted to Advanced games), one can change the text style. The
command for this is `style' and its effects are loosely modelled on the
VT100 (design of terminal). The style can be `style roman', `style
bold', `style reverse' or `style underline'. Again, remember that poor
terminals may not be able to display these, so you shouldn't hide
crucial information in them.
25 Dirty tricks
***************
!!!!! The dirtiest tricks of all are those which bypass Inform's higher
levels and program the Z-machine directly. There is an element of
danger in assembly language programming, in that some combinations of
unusual opcodes can look ugly on some incomplete or wrongly-written
interpreters: so if you're doing anything complicated, test it as widely
as possible. Note that none of the interesting effects work at all on
Standard games, only Advanced games.
The best-researched and most reliable interpreter available by far is
Mark Howell's Zip; as it's also the fastest, it will hopefully `take
over' entirely. Next comes the InfoTaskForce, which is thorough and
should give no serious trouble, but was written when the format was a
little less well understood, and so (in some ports) gets some (rare)
screen effects wrong. (It also lacks an `undo' feature, so the `undo'
verb automatically provided by the library routines won't work under
ITF.) The other two publically-available interpreters are pinfocom and
zterp, but these are unable to run Advanced games. In the last resort,
sometimes it's possible to use one of Infocom's own supplied
interpreters with a different game from that it came with; but only
sometimes, as they may have inconvenient filenames `wired into them'.
The author recommends that anyone using assembly-language features get
hold of both ITF and Zip, and test on both.
This all sounds rather unportable, though actually the core described
below is pretty reliable. But remember that the unportability does have
some genuine cause. Your game may be running on a screen which is
anything from a 64 by 9 pocket organiser up to a 132 by 48 X-window.
Anyone wanting to really push the outer limits (say, by implementing
Space Invaders or NetHack) will need to refer to `The Specification of
the Z-Machine', which also documents Inform assembly language format.
Screen tricks are the commonest. An upper-level screen (which usually
holds the status line) can be split off from the main screen:
split_window n;
creates one which is n lines tall. (This doesn't change the display,
and it can be resized at any time: but it needs to be tall enough to
include all the lines you want to write to, as otherwise the interpreter
may flounder: some will scroll the upper window, others won't.) The
main screen is numbered 0, and the upper one 1; text output is switched
between them by
set_window n;
The lower window is just a text stream whose cursor position cannot be
set: on the other hand, when it is returned to, the cursor will be where
it was before it was left. Within the upper window, the cursor can be
moved by
set_cursor line column;
where (1,1) is the top left character. Printing on the upper window
overlies printing on the lower, is always done in a fixed-space font and
does not appear in a printed transcript of the game. However, before
printing to the upper window, it is essential to change the printing
format - that is, the `buffer_mode' opcode. Before printing, execute
buffer_mode 0;
and when returning to the normal screen,
buffer_mode 1;
Otherwise, dodgy interpreters (like ITF) may continue trying to split
lines at word breaks, and make a horrid mess.
A convenient way to clear the screen is
erase_window $ffff;
but don't chance this in reverse video mode! (And don't assume that
`erase_window' can erase individual windows - it should, but may not on
bad interpreters.)
Players can be gratuitously annoyed (on most machines, anyway) by the
`beep' opcode. This is the only remotely portable sound effect.
The keyboard can be read in remarkably flexible ways, using the `aread'
and `read_char' opcodes.
aread text parse time function;
will read from the keyboard, putting text into the `text' buffer,
tokenising it onto the end of the `parse' buffer (unless this is zero),
and calling
function(time);
every `time' seconds that the user is thinking: the process ends if
ever this function returns true. Thus (by `Replace'ing the `Keyboard'
routine in the library files) you could, say, move around all the
characters every ten seconds of real time.
read_char 1 time function RESULT;
where RESULT is a variable, will store in that variable the ASCII value
of a single keypress. Once again, the `function' is called every
`time' seconds and may stop this process early. Function keys return
special values from 129 onwards, in the order: cursor up, down, left,
right, function key f1, ..., f12, keypad digit 0, ..., 9.
Leafing through the dictionary of opcodes will reveal a few other
interesting features. It's possible to change the input and output
streams which, although only Zip gets this right, may be convenient for
debugging purposes (creating scripts of all typed commands, for
example). Finally, there are opcodes which tokenise (that is, simply
compare dictionary entries against) arbitrary strings from arbitrary
dictionaries, and which translate small doses of ASCII to internal
Z-machine string format. Actually, one can avoid the need for this in
many cases, by programming the parser correctly: see, for instance, the
`Balances' game which manages without these features.
A The Inform language
*********************
This is going to be a long appendix, full of lists and tables: but it
has to appear somewhere, if only for reference. (Some technical
commands for internal use only are skipped over: see the `Technical
Manual' for details of these.)
A.1 File format
===============
When Inform reads in a file, it treats a few characters in special ways.
The character `!' means the rest of the line (up to the next new-line)
is a comment, and Inform throws it away. Tab characters are treated as
spaces. Backslashes `\' fold strings together, so that the new-line
and all subsequent spaces are ignored. New-lines have no significance;
statements (and directives) are separated by semicolons `;'.
A.2 Directives
==============
These are commands directly to the Inform compiler, like `Object'.
They can, but need not, be prefaced by a hash character, `#'. The
directives which Inform understands are:
`Abbreviate STRING'
Declare an abbreviation
`Attribute NAME'
Make a new attribute
`Class ...'
Define a new class
`Constant NAME VALUE'
Define a named constant
`End'
End compilation here
`Endif'
End of conditional compilation
`Extend ...'
Extend the grammar for an existing verb
`Fake_action NAME'
Make a new `fake action'
`Global NAME ...'
Declare a global variable
`Ifdef NAME'
Compile only if constant is defined
`Ifndef NAME'
Compile only if constant is undefined
`Ifnot'
Compile only if previous `If...' failed
`Ifv3'
Compile only for Standard games
`Ifv5'
Compile only for Advanced games
`Include FILENAME'
Include that file here
`Nearby'
Make an object inside the last `Object'
`Object'
Make an object
`Property NAME ...'
Make a new property
`Replace ROUTINE'
Don't compile this library routine
`Release NUMBER'
Set the game's Release Number
`Serial STRING'
Set the game's Serial Number
`Statusline ...'
Make the status line show score or time
`Switches SWITCHES'
Set default compilation switches
`Verb ...'
Declare the grammar for a new verb
The release number of a game (by default 1) is generally an edition
number; the serial number is the compilation date in the form 940924,
that is, yymmdd. Inform sets this automatically on machines where the
date is accessible, so the `Serial' directive is provided only for use
on machines without such an internal clock.
!!!! Conditional compilation allows code for routines which need only
exist in some `versions' of your games. For instance,
print "Welcome to the ";
#IFV3; print "Standard"; #IFNOT; print "Advanced"; #ENDIF;
print " version of Zork LVI.";
(The `#IFNOT' clause is optional.) Note the trailing semicolon: Inform
is not C! Such clauses may be nested up to 32 deep, and may contain
whole routines. They may not, however, conditionally give *part* of a
statement. Thus, for instance,
print #IFV3; "Standard"; #IFNOT; "Advanced"; #ENDIF;
is *not* legal.
!!!!! The following directives are recondite and not for public use:
Default Dictionary Listsymbols Listdict Listverbs Lowstring Stub
System_file Trace Btrace Etrace Ltrace Notrace Nobtrace Noetrace
Noltrace
A.3 Variables and arrays
========================
There are two kinds of variable, global and local (plus one special
one). Variables are all two-byte integers, which are treated as signed
when it makes sense to do so (e.g., in asking whether one is positive
or not) but not when it doesn't (e.g., when it is used as an address).
Global variables must be declared before use, by the `Global' directive:
Global VARNAME = INITIAL-VALUE
`data' SIZE
`string' SIZE
`initial' VALUE-1 ... VALUE-N
`initstr' TEXT
For instance:
Global turns = 1;
Global buffer string 120; ! text buffer holding 120 characters
Global task_scores initial 4 5 9 1 2 3 0; ! a 7-byte array
Global players_name initstr "Graham"; ! an array of 6 chars
When you declare a variable as an array (by `data', `string', `initial'
or `initstr') what actually happens is that Inform allocates as much
space as you ask for, somewhere inside the machine, and stores the
address of this array in the variable. You can get at entries of the
array by
buffer->entry buffer-->entry
which read (or write to) the `entry'-th byte (in the case of `->') or
word (for `-->'). A `data' array is initially full of zeros, while an
`initial' array contains the given (byte) values, which all have to be
constants for obvious reasons. A `string' array is a special kind of
`data' array: the first byte contains its length (in bytes), and the
rest are initially zero. `initstr' is the same but initialised to the
given string. The text here is plain ASCII, and is not encrypted as
constant strings tend to be. (This string format is used by the
parser.)
In addition, a routine can have from none up to 15 local variables.
*WARNING:* There is also a stack, but it should be tampered with only
carefully in times of dire need. Never call a variable `sp', as this
is the stack pointer, and if you must use the stack at all, be careful
not to leave values on it: or your game may crash 1000 turns later,
serving you right.
A.4 Constants
=============
Inform constants can take many forms. The obvious ones are numbers,
123 $ee05 $$11011001
being examples in decimal, hexadecimal and binary respectively. There
are also
`##Action'
(The number of) the given action
`"some text"'
(The packed address of) the given string
`'c''
(The ASCII code for) the given character
`'word''
(The byte address of) its dictionary entry
There is slight potential for confusion here: `'A'' evaluates to 65,
but `'an'' to the dictionary address of the word `an'. Note that the
construction `'sponge'' actually enters the word `sponge' into the
dictionary if it wasn't already there. These are all legal constants:
31415 -1 $ff $$1001001 'lantern' ##Look 'X'
"an emerald the size of a plover's egg"
"~Hello,~ said Peter.^~Hello, Peter,~ said Jane.^"
A.5 Quoted strings
==================
Inside the text of a string, the character `^' is replaced by a
new-line character, and the character `~' by a double-quote mark: these
both make strings much easier to type.
Inside a string (under Inform 5.3 or later) `@@'NUMBER produces the
character whose ASCII value is NUMBER, and you can use this to get
untypeable characters from foreign character sets. (Or, for example, a
literal backslash, by `@@92'.)
For the `@' string escape and other obscure constant forms such as
`#r$', see the `Technical Manual'.
A.6 Routines
============
Routines start with a `[' and end with a `]'. That is, they open with
`[' ROUTINE-NAME LOCAL-VAR-1 ... LOCAL-VAR-N`;'
giving the names of local variables for the routine ($0\leq n\leq 15$).
The routine ends with just `];'. (Routines embedded in object
definitions are the same, except that no routine-name is given, and they
may end in `],' if the object definition continues.)
The first few local variables also hold the arguments passed to the
routine when the function is called. That is, if you have a routine
[ Look from i j; ...some code...; ];
and it is called by `Look(attic);' then the local variable `from' will
initially have the value `attic'. The rest all start out at zero.
From Inform 5.3, if the first variable is given as `*' then tracing
code is compiled to print details each time the routine is called.
Function calls (that is, calls to routines) are legal with between 0 and
3 arguments, and every routine always returns a value. If execution
runs into the `];' at the bottom, that value is `true' (or 1) for an
ordinary routine, or `false' (or 0) for an embedded one.
A.7 Labels
==========
In case you want to `jump' around inside a routine, you can define
labels with a statement starting with a full stop, like `.labelname;'.
It is legal, though ill-advised, to jump out of one routine and into
another. Label names are global, so the same label name can't be used
in two different routines.
A.8 Operators
=============
Arithmetic (and other) expressions can contain the following:
`+ -'
plus, minus
`* / % & |'
times, divide, remainder, bitwise and, bitwise or
`-> -->'
byte array, word array entry
`. .& .#'
property, property address, property length
`-'
unary minus
`++ --'
incrementing and decrementing variables (as in C)
The order of precedence is as shown: i.e., those on each line are
equally potent, more potent than those above but less than those
beneath. Expressions are not allowed to contain conditions, nor
assignments: `2+(i=3/j)' is not a legal expression. Some legal
examples:
4*(x+3/y) Fish(x)+Fowl(y) lamp.time buffer->5
Note that `++' and `--' can only be applied to variables, not to
properties or array entries.
A.9 Assignments
===============
There are four forms allowed are:
VARIABLE `=' VALUE`;'
BYTE-ARRAY`->'ENTRY `=' VALUE`;'
WORD-ARRAY`-->'ENTRY `=' VALUE`;'
OBJECT`.'PROPERTY `=' VALUE`;'
For example:
i=-15-j; i=j-->1; albatross.weight = albatross.weight + 1;
(paintpot.&roomlist)-->i = location; turns++;
One can also `inc' or `dec' (increment or decrement) a variable, with
`inc score;' being equivalent to the more modern `score++;'
Although these look logical, they are not allowed:
paintpot.#roomlist = 5;
paintpot.&roomlist = array;
because one cannot change the size or address of a property in play.
*WARNING:* A division by zero error (such as `n/0' or `n%0') may crash
the game at run time.
*WARNING:* Attempting to write to a property which an object does not
have may crash the game at run time.
A.10 Conditions
===============
A simple condition is
A RELATION B
where the relation is one of
`=='
`a' equals `b'
`~='
`a' doesn't equal `b'
`< > >= <='
numeric (signed) comparisons
`has'
object `a' has attribute `b'
`hasnt'
object `a' hasnt attribute `b'
`in'
object `a' is currently held by object `b'
`notin'
... is not...
With `==' (and `~=') only, one may also write the useful construction
SOMETHING `==' V1 [`or' V2 [`or' V3]]
which is true if the first something is any of the values given. (An
idiosyncracy of Inform, for `hardware reasons', is that you can only
have three). Conditions can be combined by the `&&' and `||' operators:
CONDITION1 `&&' CONDITION2
CONDITION1 `||' CONDITION2
true if both, or either (respectively) are true. These are always
tested left to right until the outcome is known. So, for instance,
i==1 || Explode(2)==2
does not call `Explode' if `i' is 2. Examples of legal conditions are:
i==1 or 2 or 3
door has open || (door has locked && key in player)
A.11 Built-in functions
=======================
A very few functions are built into the language of Inform itself
(rather than written out longhand in the library files), but they
behave like any other routines. They are:
`parent(obj)'
parent of object
`sibling(obj)'
sibling of object
`child(obj)'
eldest child of object
`children(obj)'
number of (direct) children of object
`eldest(obj)'
same as `child'
`youngest(obj)'
youngest child of object
`elder(obj)'
elder sibling of object
`younger(obj)'
same as `sibling'
`random(x)'
uniformly random number between 1 and $x$
`indirect(addr)'
call routine with address `addr'
*WARNING:* `random(0)' may cause a division by zero error.
A.12 Printing commands
======================
A string on its own, such as
"The world explodes in a puff of garlic.";
is printed, with a new-line, and the current routine is returned from
with return value `true', i.e., 1. In addition:
`new_line'
prints a new-line
`print ...'
prints the given things
`print_ret ...'
prints, new-lines and returns 1
`spaces n'
prints n spaces
`print_addr a'
print string at byte address `a'
`print_paddr a'
print string at packed address `a'
`font on/off'
turns proportional fonts on/off
`style ...'
in Advanced games, sets text style
`box "s1" ... "sn"'
in Advanced games, puts up display box
`inversion'
prints out the current Inform version number
`print' and `print_ret' take a comma-separated list of things to print
out, which can be: `"strings"', `char n' (print the character with this
ASCII value) or `variable' (print out the variable as a signed number).
The text `style' can be any of
roman reverse bold underline
A.13 Manipulating objects
=========================
`remove obj'
removes object from the tree
`move o1 to o2'
moves `o1' to become youngest child of `o2'
`give obj a1 ... an'
gives attributes to `obj'
Attributes beginning with a `~' are taken away rather than given.
A.14 Returning from routines
============================
`return'
Return true, i.e., 1
`return x'
Return value $x$
`rtrue'
Return true, i.e., 1
`rfalse'
Return false, i.e., 0
A.15 Blocks of code
===================
A block of code may be a single instruction or a series of several, in
which case it must be enclosed in braces `{' and `}'. Thus, for
instance, in
if (i==1)
print "The water rises!";
if (i==2) {
print "The water rises further...";
water++;
}
the `if' statements contain a block of code each. Blocks can be nested
inside each other up to 32 deep. An `if' statement (for example) is a
single statement even when it contains a great deal of code in its
block: so, for example,
if (i>1)
if (water<10)
"The water is beginning to worry you.";
is legal. (One small exception: an `if' followed by an `else' counts
as two statements, and this means Inform handles hanging elses rather
poorly: so it's wise to brace so that `else' is clearly unambiguous.)
A.16 Control constructs
=======================
Inform provides:
`if' CONDITION BLOCK1 `[else' BLOCK2`]'
`while' CONDITION BLOCK
`do' BLOCK `until' CONDITION
`for ('INITIALISE`:'TEST`:'EACH TIME`)'
`objectloop ('VARIABLE `in' OBJECT`)'
`objectloop ('VARIABLE `from' OBJECT`)'
`objectloop ('VARIABLE `near' OBJECT`)'
`break'
`jump' LABEL
Some of these will be familiar to most. The `for' construct is
essentially the same as that in C, except for the colons `:' (which in
C would be semicolons). Its carries out the initial assignment(s),
then executes the code for as long as the condition holds, executing the
end assignment after each pass through the code. For instance,
for (i=1:i<=10:i++) print i, " ";
counts to 10. All three clauses are optional, and the empty condition
is always true; multiple assignments can be made. For instance:
for (i=0,j=10:i<10:i++,j--) print i, " + ", j, " = ", i+j, "^";
for (::) print "Ha!^";
the latter laughing maniacally forever.
`break' breaks out of the current loop (not quite the same as breaking
out the current block of code: `if' statements don't count as loops in
this regard).
`objectloop' goes through the object tree, and is extremely useful.
`from' means from the given object through its siblings; `in' means
through all children of the given object, and `near' means through all
children of the parent of the object. For instance, the following do
the same thing:
objectloop (x in lamp) { ... }
for (x=child(lamp): x~=0: x=sibling(x)) { ... }
Note that the library creates a variable called `top_object' holding
the highest existing object number: so a way to loop over every object
defined in your own code is
for (i=player+1: i<=top_object: i++) ...
since `player' is always set to `selfobj', which is the last object
created by the library.
*WARNING:* When looping through the object tree, be careful if you are
altering it at the same time. For instance,
objectloop (x in rucksack) remove x;
is likely to go horribly wrong - it's safer not to cut down a tree
while actually climbing it. The safe way is to keep lopping branches
off,
while (child(x)~=0) remove child(x);
A.16.1 Exercise: primes
-----------------------
Write a routine to print out prime factorisations of numbers from 2 to
100.
*Note Answer (primes)::.
A.17 Invoking actions
=====================
The commands
`<' ACTION `['FIRST-OBJECT `['SECOND-OBJECT`]]' `>'
`<<' ACTION `['FIRST-OBJECT `['SECOND-OBJECT`]]' `>>'
cause the given actions to take place. In the latter case, the current
routine then returns 1, or true.
B Library, properties and attributes
************************************
A Model must be built which will get everything in without a
clash; and it can do this only by becoming intricate, by mediating
its unity through a great, and finely ordered, multiplicity.
- C. S. Lewis (1898-1963), `The Discarded Image'
B.1 Library objects
===================
The library defines the following special objects:
`compass'
To contain the directions. A direction object provides a
`door_dir' property, and should have the `direction' attribute. A
compass direction with `enterable', if there is one (which there
usually isn't), will have an `Enter' action converted to `Go'.
`n_obj'
Both the object signifying the abstract concept of `northness',
and the `north wall' of the current room. (Thus, if a player
types `examine the north wall' then the action `Examine n_obj'
will be generated.) Its `door_dir' property holds the direction
property it corresponds to (`n_to').
`s_obj'
`e_obj'
`w_obj'
`ne_obj'
`nw_obj'
`se_obj'
`sw_obj'
Similar.
`u_obj'
`d_obj'
`in_obj'
`out_obj'
Similar: the parser uses these if the player refers to `ceiling',
`floor'. (`in_obj' and `out_obj' differ slightly, because `in'
and `out' are verbs with other effects in some cases.)
`thedark'
A pseudo-room representing `being in darkness'. `location' is then
set to this room, but the player object is not moved to it. Its
`description' can be changed to whatever `It is dark here' message
is desired.
`selfobj'
The player object. However, do not refer to `selfobj' directly:
always refer to `player', a variable whose value is usually indeed
`selfobj' but which might become `green_frog' if the player is
transformed into one.
B.2 Library attributes
======================
Here is a concise account of all the normal rules concerning all the
library's attributes. (Except that: rules about how the parser sorts
out ambiguities are far too complicated to include here, but should not
concern designers anyway; and the definitions of `scope' and `darkness'
are given in the main text.) Of course these rules are all modifiable
using suitable `before' and `after' rules. But a good deal of
pragmatism has gone into their design, or rather evolution.
`absent'
A `floating object' (one with a `found_in' property, which can
appear in many different rooms) which is `absent' will no longer
appear in the game.
`animate'
`Is alive (human or animal)'. Can be spoken to in `richard, hello'
style; matches the `creature' token in grammar; picks up `him' or
`her' (according to gender) rather than `it', likewise `his'; an
object the player is changed into becomes `animate'; some messages
read `on whom', etc., instead of `on which'; can't be taken; its
subobjects `belong to' it rather than `are part of' it; messages
don't assume it can be `touched' or `squeezed' as an ordinary
object can; the actions `Attack', `ThrowAt' are diverted to `life'
rather than rejected as being `futile violence'.
`clothing'
Can be worn.
`concealed'
`Concealed from view but present'. The player's object has this;
an object which was the player until `ChangePlayer' happened, loses
this property; a `concealed' `door' can't be entered; does not
appear in room descriptions.
`container'
Affects scope (*note Scope::.) and light (*note Light::.); object
lists recurse through it if `open' (or `transparent'); may be
described as closed, open, locked, empty; a possession will give
it a `LetGo' action if the player tries to remove it, or a
`Receive' if something is put in; things can be taken or removed
from it, or inserted into it, but only if it is `open'; likewise
for `transfer' and `empty'; room descriptions describe using
`when_open' or `when_closed' if given; if there is no defined
`description', an `Examine' causes the contents to be searched
(i.e., written out) rather than a message `You see nothing special
about...'; `Search' only reveals the contents of `container's,
otherwise saying `You find nothing'.
`direction'
If the player tries to walk this way or enter it, a `Go' rather
than `Enter' action is generated. (Used only in one line of
grammar, but a crucial one.)
`door'
`Is a door or bridge'. Room descriptions describe using
`when_open' or `when_closed' if given; and an `Enter' action
becomes a `Go' action. If a `Go' has to go through this object,
then: if `concealed', the player `can't go that way'; if not
`open', then the player is told either that this cannot be
ascended or descended (if the player tried `up' or `down' which
went through it), or that it is in the way (otherwise); but if
neither, then its `door_to' property is consulted to see where it
leads; finally, if this is zero, then it is said to `lead nowhere'
(a safety precaution) and otherwise the player actually moves to
the location.
`edible'
Can be eaten (and thus removed from game).
`enterable'
Affects scope (*note Scope::.) and light (*note Light::.); only an
`enterable' on the floor can be entered.
`female'
Only applies to `animate's (and cannot have a `found_in' list for
arcane reasons), and only affects the parser's use of pronouns: it
says `her' is appropriate but `him' and `his' are not.
`general'
A general-purpose attribute, defined by the library but never
looked at or altered by it. This is left free to mean something
different for each object: often used by programmers for something
like `the puzzle for this object has been solved'.
`light'
`Is giving off light.' *Note Light::. Also: the parser
understands `lit', `lighted', `unlit' using this; inventories will
say `(providing light)' of it, and so will room descriptions if
the current `location' is ordinarily dark; it will never be
automatically put away into the player's `SACK_OBJECT' (as it
might plausibly be inflammable or the main light source).
`lockable'
Can be locked or unlocked by a player holding its key object,
which is in the property `with_key'; if a `container' and also
`locked', may be called `locked' in inventories.
`locked'
Can't be opened. If a `container' and also `locked', may be
called `locked' in inventories.
`moved'
`Has once been held by the player'. Objects (immediately) owned
by the player after `Initialise' has run are given it; at the end
of each turn, if an item is newly held by the player and is
`scored', it is given `moved' and `OBJECT_SCORE' points are
awarded; an object's `initial' message only appears in room
descriptions if it is unmoved.
`on'
`Switched on.' A `switchable' object with this is described by
`with_on' in room descriptions; it will be called `switched on' by
`Examine'.
`open'
`Open door or container'. Affects scope (*note Scope::.) and light
(*note Light::.), and: lists (such as inventories) recurse through
an `open' `container'; if a `container', called open by some
descriptions; things can be taken or removed from an `open'
`container'; similarly inserted, transferred or emptied. An
`open' `door' can be entered. Described by `when_open' in room
descriptions.
`openable'
Can be opened or closed, unless `locked'.
`proper'
Its short name is a proper noun, and never preceded by `the' or
`The'. The player's object must have this (so something changed
into will be given it).
`scenery'
Not listed (by the library) in room descriptions, `not portable'
to be taken; `you are unable to' pull, push, or turn it.
`scored'
The player gets `OBJECT_SCORE' points for picking it up for the
first time; or, if a room, `ROOM_SCORE' points for visiting it for
the first time.
`static'
`Fixed in place' if player tries to take, remove, pull, push or
turn.
`supporter'
`Things can be put on top of it'. Affects scope (*note Scope::.)
and light (*note Light::.); object lists recurse through it; a
possession will give it a `LetGo' action if the player tries to
remove it, or a `Receive' if something is put in; things can be
taken or removed from it, or put on it; likewise for transfers; a
player inside it is said to be `on' rather than `in' it; room
descriptions list its contents in separate paragraphs if it is
itself listed.
`switchable'
Can be switched on or off; listed as such by `Examine'; described
using `when_on' or `when_off' in room descriptions.
`talkable'
Player can talk to this object in `thing, do this' style. (This is
useful for microphones and the like.)
`transparent'
Affects scope (*note Scope::.) and light (*note Light::.). It
roughly means `contents always visible'. A `transparent'
container is treated as if it were `open' for printing of contents.
`visited'
Given to a room when a `Look' first happens there: if this room is
`scored' then `ROOM_SCORE' points are awarded. Affects whether
room descriptions are abbreviated or not.
`workflag'
Temporary flag used by Inform internals, also available to outside
routines; can be used to select items for some lists printed by
`WriteListFrom'.
`worn'
`Item of clothing being worn'. Should only be held by an object
being immediately carried by player. Affects inventories; doesn't
count towards the limit of `MAX_CARRIED'; won't be automatically
put away into the `SACK_OBJECT'; a `Drop' action will cause a
`Disrobe' action first; so will `PutOn' or `Insert'; you can't
insert or remove something from a `worn' container.
Note that very few attributes sensibly apply to rooms: only really
`light', `scored' and `visited', together with `general' if you choose
to use it. Note also that an object cannot be both a `container' and a
`supporter'; and that the old attribute `autosearch', which was in
earlier releases, has been withdrawn as obselete.
B.3 Library properties
======================
Next, definitions of properties which sensibly apply to `objects'
(meaning, things which are not rooms):
`after'
List of rule routines to be applied in sequence after an action
takes place. (For a room, receives all actions in that room; for a
player-object, all actions by the player as that object.)
`article'
Indefinite article for object (defaults to `a').
`before'
List of rule routines to be applied in sequence before an action
takes place. (For a room, receives all actions in that room; for a
player-object, all actions by the player as that object.)
`cant_go'
Message to print (or routine to print one) when a player tries to
go in an impossible direction from this room (defaults to `You
can't go that way.')
`capacity'
Number of objects a `container' or `supporter' can hold (defaults
to 100); number of things the player can carry (when the player is
this object), initially set to `MAX_CARRIED' for the standard
player object.
`daemon'
Routine to run each turn (once activated by a `StartDaemon' and
until stopped by a `StopDaemon'). Note: the same object cannot
have both a `daemon' and a `time_out'.
`describe'
Routine (only) to describe object: over-rides a room's
`description' to give main room description, if set; over-rides an
object's usual listing when it appears in a room description.
`description'
`Examine' message, or routine to print one out, for an object; main
room description, or routine to print one, for a room.
`door_dir'
Direction (that is, direction property, such as `n_to', not
direction object) that a `door' goes via, or a routine which
returns this information; also used for direction objects. *Note
Compass: Library objects.
`door_to'
Room that a `door' connects to, or routine returning this; a value
of 0 means `leads nowhere' (but this is not the ideal way to
implement such a door).
`each_turn'
String to print, or list of routines to run, each turn that the
object is in scope, after all timers and daemons have run.
`found_in'
List of rooms in which a `floating object' (that is, one present in
multiple rooms) is found, unless it has `absent', in which case it
is nowhere to be found.
`initial'
Initial description of object not yet picked up, or routine to
print one.
`invent'
Routine to change its inventory listing. *Note Inventories::.
`life'
List of rule routines to be applied in sequence after an action
making sense for an `animate' object takes place.
`name'
List of dictionary words referring to an object (note: uniquely,
these dictionary words are given in double quotes `"thus"',
whereas in all other circumstances they would be `'thus''; part
tradition, part defence against `'a'' single-letter ambiguity).
For a room, lists dictionary words which `do not need to be
referred to in this game'.
`number'
A general purpose property left free: conventionally holding a
number like `number of turns' battery power left'. (Except: an
object to be used as a player-object must provide one, and it
isn't left free.)
`parse_name'
Routine (only) to parse object's name (this overrides the `name'
and is also used in determining if two objects are describably
identical). *Note Naming of names::.
`plural'
The plural name of an object (when in the presence of others like
it), or routine to print it.
`short_name'
The short name of an object (like `brass lamp'), or routine to
print it.
`time_left'
Number of turns left until timer (if set, which must be done using
`StartTimer') goes off.
`time_out'
Routine to run when timer goes off (having been set by `StartTimer'
and not in the mean time stopped by `StopTimer'). Note: the same
object cannot have both a `daemon' and a `time_out'.
`when_closed'
`Look' description of something closed (`door' or `container'), or
routine to print one.
`when_open'
`Look' description of something open (`door' or `container'), or
routine to print one.
`when_off'
`Look' description of a `switchable' which isn't `on', or routine
to print one.
`when_on'
`Look' description of a `switchable' which is `on', or routine to
print one.
`with_key'
Key object needed to lock/unlock a `lockable' object; player must
explicitly name it as the key being used and be holding it at the
time to use it. May be 0, in which case no key fits.
together with the properties
n_to s_to ne_to se_to u_to d_to
e_to w_to nw_to sw_to in_to out_to
each of which can be map connections to other routines, or strings (in
which case they are printed if the player tries to go that way, but the
move is not allowed and no further action is taken), or routines (in
which case they are called to decide whether the map connection exists
or not, and if so where it leads).
*WARNING:* Do not confuse `n_to' and so on with the 12 direction
objects, `n_obj' et al.
C All the entry points, library routines and constants
******************************************************
C.1 Library entry points
========================
Entry points are routines which you can provide, if you choose to, and
which are called by the library routines to give you the option of
changing the rules. All games *must* define an `Initialise' routine,
which is obliged to set the `location' variable to a room; the rest are
optional.
`AfterLife'
When the player has died (a condition signalled by the variable
`deadflag' being set to a non-zero value other than 2, which
indicates winning), this routine is called: by setting `deadflag=0'
again it can resurrect the player.
`Amusing'
Called to provide an `afterword' for players who have won (for
instance, it might advertise some features which a successful
player might never have noticed). (But only if you have defined
the constant `AMUSING_PROVIDED' in your own code.)
`DarkToDark'
Called when a player goes from one dark room into another one; a
good excuse to kill the player off in anyone's book.
`DeathMessage'
Prints up `You have died' style messages, for `deadflag' values of
3 or more. (If you choose ever to set `deadflag' to such.)
`GamePostRoutine'
A kind of super-`after' rule, which applies to all actions in the
game, whatever they are: use only in the last resort.
`GamePreRoutine'
A kind of super-`before' rule, which applies to all actions in the
game, whatever they are: use only in the last resort.
`Initialise'
A compulsory routine, which must set `location' and is convenient
for miscellaneous initialising, perhaps for random settings.
`InScope'
An opportunity to place extra items in scope during parsing, or to
change the scope altogether. (If `et_flag' is 1 when this is
called, the scope is being worked out for `each_turn' reasons;
otherwise for everyday parsing.)
`LookRoutine'
Called at the end of every `Look' description.
`NewRoom'
Called when the room changes, before any description of it is
printed. (This happens no matter how the change of room occurred.)
`ParseNumber'
An opportunity to parse numbers in a different (or additional) way.
`ParserError'
The chance to print different parser error messages (like `I don't
understand that sentence').
`PrintRank'
Completes the printing of the score. (You might want to change
this, so as to make the ranks something like `junior astronaut' or
`master catburglar' or whatever suits your game.)
`PrintVerb'
A chance to change the verb printed out in a parser question (like
`What do you want to (whatever)?') in case an unusual verb via
`UnknownVerb' has been constructed. Returns true (or 1) if it has
printed something.
`PrintTaskName'
Prints the name of a game task (such as `driving the car').
`TimePasses'
Called after every turn (but not, for instance, after a command
like `score' or `save'). It's much more elegant to use timers and
daemons, or `each_turn' routines for individual rooms - using this
is a last resort.
`UnknownVerb'
Called by the parser when it hits an unknown verb, so that you can
transform it into a known one.
C.2 Library routines
====================
Library routines which are `open to the public':
`Achieved(task)'
Indicate the `task' is achieved (which only awards score the first
time).
`AllowPushDir()'
Signal that an attempt to push an object from one place to another
should be allowed.
`CDefArt(object)'
Print the capitalised definite article and short name of `object'.
`ChangePlayer(object, flag)'
Cause the player at the keyboard to play as the given object,
which must have a `number' property supplied. If the `flag' is
set to 1, then subsequently print messages like `(as Ford
Prefect)' in room description headers. This routine, however,
prints nothing itself.
`DefArt(object)'
Print the definite article and short name of `object'.
`DoMenu(text,routine,routine)'
Produce a menu.
`EnglishNumber(x)'
Prints out `x' in English (e.g., `two hundred and seventy-seven').
`HasLightSource(object)'
Returns true if `object' `has light'.
`InDefArt(object)'
Print the indefinite article and short name of `object'.
`NextWord()'
Returns the next dictionary word in the player's input, moving the
word number `wn' on by one. Returns -1 if the player's input has
run out, and 0 if the word is not in the dictionary.
`OffersLight(object)'
Returns true if `object' `offers light'.
`PlaceInScope(object)'
Puts `object' into scope for the parser.
`PlayerTo(place, flag)'
Move the player to `place'. Unless `flag' is given and is 1,
describe the player's surroundings.
`PrintShortName(object)'
Print the short name of `object'. (This is protected against
`object' having a meaningless value.)
`ScopeWithin(object)'
Puts the contents of `object' into scope, recursing downward
according to the usual scope rules.
`SetTime(time,rate)'
Set the game clock (a 24-hour clock) to the given `time' (in
seconds since the start of the day), to run at the given rate
RATE: 0 means it does not run; if RATE > 0 then RATE seconds pass
every turn; if RATE < 0 then -RATE turns pass every second.
`ScopeWithin(object)'
Puts the contents of `object', recursing downward according to the
usual scope rules.
`StartDaemon(object)'
Makes the daemon of `object' active, so that its `daemon' routine
will be called every turn.
`StartTimer(object, time)'
Starts the timer of `object', set to go off in `time' turns, at
which time its `time_out' routine will be called (it must provide
a `time_left' property).
`StopDaemon(object)'
Makes the daemon of `object' inactive, so that its `daemon'
routine is no longer called.
`StopTimer(object)'
Stops the timer of `object', so that it won't go off after all.
`TryNumber(wordnum)'
Tries to parse the word at `wordnum' as a number (recognising
decimal numbers and English ones from `one' to `twenty'), returning
-1000 if it fails altogether, or the number. Values exceeding
10000 are rounded down to 10000.
`WriteListFrom(object, style)'
Write a list of `object' and its siblings, with `style' a bitmap
of options.
`YesOrNo()'
Assuming that a question has already been printed, wait for the
player to type `yes' or `no', returning true or false accordingly.
C.3 Library constants
=====================
And, finally, the constants:
`AMUSING_PROVIDED'
To indicate that an `Amusing' routine is provided.
`DEBUG'
To include the special `debugging' verbs.
`Headline'
Style of game and copyright message.
`MAX_CARRIED'
Maximum number of (direct) possessions the player can carry.
`MAX_SCORE'
Maximum game score.
`MAX_TIMERS'
Maximum number of timers or daemons active at any one time
(defaults to 32).
`NUMBER_TASKS'
Number of `tasks' to perform.
`OBJECT_SCORE'
Score for picking up a `scored' object for the first time.
`ROOM_SCORE'
Score for visiting up a `scored' room for the first time.
`SACK_OBJECT'
Object which acts as a `rucksack', into which the game
automatically tidies away things for the player.
`Story'
Story name, conventionally in CAPITAL LETTERS.
`TASKS_PROVIDED'
To indicate that `tasks' are provided.
D All the Inform error messages
*******************************
Inform can produce about 230 different error messages. The error
messages were tidied-up and made more consistent in Inform 5.4, which
the text here comes from, but earlier editions were similar. Since
interpreters can in some cases crash horribly when given incorrect
files, Inform never writes a file which caused an error, though it will
permit files which incurred only warnings.
D.1 Fatal errors
================
To begin with, fatal errors (which stop Inform in its tracks) come in
three kinds, the first containing only this one:
Too many errors: giving up
After 100 errors, Inform stops (in case it has been given the wrong
source file altogether). Secondly, file input/output can go wrong.
Most commonly, Inform has the wrong filename:
Couldn't open input file <filename>
Couldn't open output file <filename>
Couldn't open transcript file <filename>
Couldn't open debugging information file <filename>
Couldn't open temporary file 1 <filename>
Couldn't open temporary file 2 <filename>
Too many files have included each other: increase #define MAX_INCLUSION_DEPTH
(Temporary files are used (on most machines) for temporary storage space
during compilation. They are removed afterwards.) The last error only
occurs if 5 files all include each other. Increasing this `#define'
means re-compiling Inform from its C source, a drastic measure. The
remaining file-handling errors usually mean that the disc is full:
something has gone wrong with an already-open file.
I/O failure: couldn't read from source file
I/O failure: couldn't write to temporary file 1
I/O failure: couldn't reopen temporary file 1
I/O failure: couldn't read from temporary file 1
I/O failure: couldn't write to temporary file 2
I/O failure: couldn't reopen temporary file 2
I/O failure: couldn't read from temporary file 2
I/O failure: couldn't write to story file
I/O failure: couldn't write to transcript file
I/O failure: can't write to debugging information file
The third class of fatal error is Inform running out of memory. It
might fail drastically, having not enough memory to get started, as
follows...
Couldn't allocate memory
Couldn't allocate memory for an array
(There are four similar `hallocate' errors unique to the PC `Quick C'
port.) More often it will run out in the course of compilation, like
so:
The memory setting <setting> (which is <value> at present)
has been exceeded.
Try running Inform again with $<setting>=<some-larger-number>
on the command line.
For details of the memory settings, *Note Memory settings::.
D.2 Errors
==========
There are a few conventions. Anything in double-quotes is a display
from your source code; other strings are in single-quotes. A message
like
Expected ... but found "..."
means that Inform expected something different from what it found; if it
doesn't say what it found, this usually means it found nothing (i.e.,
the statement was incomplete). Messages in the form
No such ... as "..."
Not a ...: "..."
mean that a name is unrecognised in the former case (say, a typing error
might produce this), or is recognised but means something else in the
latter case (an attempt to use a routine where a property is expected
would give such an error).
To begin with, the source-code format may go awry:
Too many tokens on line (note: to increase the maximum, set
$MAX_TOKENS=some-bigger-number on the Inform command line)
Line too long (note: to increase the maximum length, set
$BUFFER_LENGTH=some-bigger-number on the Inform command line)
Too much text for one pair of "s to hold
Too much text for one pair of 's to hold
Open quotes " expected for text but found <text>
Close quotes " expected for text but found <text>
(Usually `BUFFER_LENGTH' allows about 2000 characters per line.) When
giving something (such as an object) an internal name, there are rules
to be obeyed; for instance, you can't give an object the same name as a
property already declared:
Symbol name expected
Symbol names are not permitted to start with an '_'
Symbol name is too long: <text>
Duplicated symbol name: <text>
At the top level, the most common `no such command' error is
Expected an assignment, command, directive or opcode but found <text>
which means Inform didn't even understand the first word of the command.
Directives to Inform can produce the following errors:
All 32 attributes already declared (compile as Advanced game to
get an extra 16)
All 48 attributes already declared
All 30 properties already declared (compile as Advanced game to
get an extra 32)
All 62 properties already declared
Expected an attribute name after 'alias'
Expected a property name after 'alias'
'alias' incompatible with 'long'
'alias' incompatible with 'additive'
'alias' refers to undefined attribute <text>
'alias' refers to undefined property <text>
All 235 global variables already declared
Expected 'string', 'data', 'initial', 'initstr' or '=' but found <text>
Use of `alias' is rare (except inside the library files). The last of
these errors means that a global variable has been wrongly initialised,
and a common cause of this is typing, say, `global trolls 5;' instead
of `global trolls = 5;'.
'*' divider expected, but found <text>
No such token as <text>
Expected '=' after 'scope' but found <text>
Expected routine after 'scope=' but found <text>
Expected routine after 'noun=' but found <text>
'=' is only legal here as 'noun=Routine'
'->' clause missing
No such action routine as <text>
Not an action: <text>
Too many lines of grammar for verb: increase #define MAX_LINES_PER_VERB
There is no previous grammar for the verb <text>
Two different verb definitions refer to <text>
Expected 'replace', 'last' or 'first' but found <text>
These are the grammatical errors, the last three concerning `extend'ed
verb definitions. Normally one gets 16 grammar lines per verb. It's
probably better to write grammar more carefully (using routines to
parse adjectives more carefully, for instance) than to exceed this, as
the game parser will otherwise slow down. The object/class definition
errors are largely self-explanatory:
Object/class definition finishes with ','
Two commas ',' in a row in object/class definition
No such attribute as <text>
No such class as <text>
Expected 'with', 'has' or 'class' in object/class definition but found <text>
Expected an (internal) name for object but found the string <text>
An object must be defined after the one which contains it: (so far)
there is no such object as <text>
Not an object: <text>
No such property as <text>
Not a property: <text>
Miscellaneous errors complete the list produced by mostly-uncommon
directives:
Expected ';' after 'include <file>' but found <text>
A 'switches' directive must come before constant definitions
Expected 'score' or 'time' after 'statusline' but found <text>
The serial number must be a 6-digit date in double-quotes
The version number must be 3 to 6: 3 for Standard games and 5 for Advanced
Expected 'on' or 'off' after 'font' but found <text>
Defaulted constants can't be strings
Must specify 0 to 3 variables in 'stub' routine
Expected 'full' or nothing after 'etrace' but found <text>
Too many abbreviations declared
All abbreviations must be declared together
It's not worth abbreviating <text>
Expected a 'string' value
No such directive as <text>
Conditional compilation happens as a result of one of `#IFDEF', `#IFV3'
or `#IFV5'. (The former tests whether a constant is defined; the
latter test for version-3 (Standard) games or version-5 (Advanced)
games.) An `#IFNOT' section is optional but the closing `#ENDIF' is
compulsory. In these error messages `#IF...' means any of the three
opening clauses.
'#IF...' nested too deeply: increase #define MAX_IFDEF_DEPTH
'#ENDIF' without matching '#IF...'
'#IFNOT' without matching '#IF...'
Two '#IFNOT's in the same '#IF...'
End of file reached inside '#IF...'
Routines begin with a `[' and some local variables and end with `]':
Routine has more than 15 local variables
The earliest-defined routine is not allowed to have local variables
Expected local variable but found ',' or ':' (probably the ';' after the
'[ ...' line was forgotten)
Misplaced ']'
Comma ',' after ']' can only be used inside object/class definitions
Expected ',' or ';' after ']' but found <text>
Expected ';' after ']' but found <text>
The error messages for expressions are fairly simple. Note, however,
that one is not allowed to type, say, `lamp.number++;' but must instead
write `lamp.number = lamp.number + 1;' (the `++' and `--' operators can
only be applied to variables, not properties).
Expected condition but found expression
Unexpected condition
Expected an assignment but found <text>
Expected an assignment but found an expression
Attempt to use void as a value
Attempt to use an assignment as a value
Attempt to use a condition as a value
Operator has too few arguments
Operator has too many arguments
'++' and '--' can only apply directly to variables
At most three values can be separated by 'or'
'or' can only be used with the conditions '==' and '~='
Too many brackets '(' in expression
Brackets '(' too deeply nested
Missing bracket ')' in function call
Spurious comma ','
Misplaced comma ','
Wrong number of arguments to system function
'children' takes a single argument
'youngest' takes a single argument
'elder' takes a single argument
'indirect' takes at least one argument
Type mismatch in argument <text>
A function may be called with at most 3 arguments
Malformed statement 'Function(...);'
Spurious terms after function call
Spurious terms after assignment
Spurious terms after expression
Next, constants. Note that dictionary words cannot start with a
non-alphabetic character, which means that Infocom-style `debugging
verbs' which traditionally begin with a `#' are not allowed.
No such variable as <text>
No such constant as <text>
Not a constant: <text>
Reserved word as constant: <text>
No such routine as <text>
Dictionary words must begin with a letter of the alphabet
Dictionary word not found for constant <text>
Loop constructs: note that `old-style' `for' loops are a rather
obselete form (e.g., `for i 1 to 10'), and the more flexible style `for
(i=1:i<=10:i++)' is now preferred.
'if' statement with more than one 'else'
'else' attached to a loop block
'for' loops too deeply nested
':' expected in 'for' loop
Second ':' expected in 'for' loop
Concluding ')' expected in 'for' loop
'to' missing in old-style 'for' loop
'to' expected in old-style 'for' loop
Final value missing in old-style 'for' loop
'{' required after an old-style 'for' loop
Old-style 'for' loops must have simple final values
Open bracket '(' expected in 'objectloop'
'objectloop' must be 'from', 'near' or 'in' something
Close bracket ')' expected in 'objectloop'
Braces '{' are compulsory unless the condition is bracketed
Unmatched '}' found
Brace mismatch in previous routine
Inform checks the level of braces when a routine closes so that it can
recover as quickly as possible from a mismatch; this last error means at
least one brace is still open when the routine finishes.
The object to 'give' to must be a variable or constant
Expected some attributes to 'give'
Expected 'to <object>' in 'move'
Expected 'to' in 'move' but found <text>
Expected ',' in 'print' list but found <text>
Expected 'style' to be 'roman', 'bold', 'underline' or 'reverse'
but found <text>
Expected a parse buffer for 'read'
Expected some properties to 'write'
The object to 'write' must be a variable or constant
Expected property value to 'write'
Expected 'byte' or 'word' in 'put'
Expected 'byte' or 'word' in 'put' but found <text>
These are miscellaneous commands and `put' and `write' are quite
obselete. Only action commands like `<Take brass_lantern>' remain:
Action name too long or a string: perhaps a statement accidentally ended
with a comma?
Action commands must take the form '< Action ... >'
The longest action command allowed is '<Action noun second>'
Angle brackets do not match
Action given in constant does not exist
Action name over 60 characters long: <text>
Expected ':' (after action) but found <text>
The first of these may be caused by something like:
"You load the crossbow bolt.",
Drop: "The bolt falls to the floor with a thump.";
where a comma has been typed instead of a semicolon after the first
string, so that Inform thinks you are giving a rule for two actions, one
being `Drop' and the other apparently called `"You load the crossbow
bolt."'.
D.3 Internal and assembler errors
=================================
By now we have descended to the ninth circle of Inform: the assembler.
These errors are fairly unmysterious (if only because they seldom
happen), but sometimes one is told something odd like
No such label as _f456
which is caused by Inform failing to recover properly from a previous
brace mismatch error. Just ignore this and fix the earlier error (which
will also have been reported). The `no such variable' error is
occasionally seen when an unknown variable is first referred to by being
written to.
No such assembly opcode as <text>
Too many arguments
Can't store to that (no such variable)
Branch too far forward: use '?'
No such return condition for branch
Can't branch to a routine, only to a label
No such label as <text>
Not a label: <text>
To conclude with, there are a few internal error messages:
A label has moved between passes because of a low-level error just before
(perhaps an improper use of a routine address as a constant)
Object has altered in memory usage between passes: perhaps an attempt to
use a routine name as value of a small property
Duplicated system symbol name <text>
Internal error - unknown directive code
Internal error - unknown compiler code
The last three should not happen. The first two occasionally do, and
cause some confusion. Inform performs two tests regularly as a
safeguard to make sure that code has not come out substantially
different in its two passes. The first failure occurs in code, the
second for object/class definitions. Whatever caused this should be
something unusual, low-level and just before the error occurred: if you
get these errors with innocent high-level code, then probably Inform
should provide a suitable error message, so please email the author with
a sample of offending code.
D.4 Warnings
============
Inform can produce any number of warnings without shutting down. Note
that Inform tries to give only warnings when it hits Advanced-game
features in what is to be a Standard game, for the sake of portability.
Nevertheless it is probably better to use `#IFV3' and `#IFV5' clauses
around any pieces of code which are to be different in these two
versions (if, indeed, you want two different versions).
Local variable unused: <text>
Since it is defined before inclusion of the library, game-play will begin
not at 'Main' but at the routine <text>
Ignoring Advanced-game status routine
Ignoring this Advanced-game command
Missing ','? Property data seems to contain the property name <text>
Standard-game limit of 8 bytes per property exceeded (use Advanced to get
64), so truncating property <text>
Ignoring Advanced-game opcode <text>
Ignoring Standard-game opcode <text>
E Compiler options and memory settings
**************************************
I was promised a horse, but what I got instead
was a tail, with a horse hung from it almost dead.
-- Palladas of Alexandria, translated by Tony Harrison
*The reader is warned that some details in this section are slightly
different on different machines.*
E.1 Compiler options
====================
On most machines, Inform is run from the command line, by a command like
inform -xv5 balances
and simply typing `inform' will produce a good deal of help information
about the command line options available. The command line syntax is
`inform' SWITCHES SETTINGS SOURCE FILE OUTPUT FILE
where only the SOURCE FILE is mandatory. (By default, the full names
to give the source and output files are derived in a way suitable for
the machine Inform is running on: on a PC, for instance, `advent' may
be understood as asking to compile `advent.inf' to `advent.z5'.)
The switches are given in one or more groups, preceded by a minus sign
`-' in the usual Unix command-line style. The current list of legal
switches is:
a list assembly-level instructions compiled
b give statistics and/or line/object list in both passes
c more concise error messages
d contract double spaces after full stops in text
e economy mode (slower): make use of declared abbreviations
f frequencies mode: show how useful abbreviations are
g with debugging code: traces all function calls
h print this information
i ignore default switches set within the file
j list objects as constructed
k output Infix debugging information to "Game_Debug"
l list all assembly lines
m say how much memory has been allocated
n print numbers of properties and attributes
o print offset addresses
p give percentage breakdown of story file
r record all the text to "Game_Text"
s give statistics
t trace Z-code assembly
u work out most useful abbreviations
v3 compile to version-3 (Standard) story file
v4 compile to version-4 (Plus) story file
v5 compile to version-5 (Advanced) story file
v6 compile to version-6 (graphical) story file
w disable warning messages
x print # for every 100 lines compiled (in both passes)
z print memory map of the Z-machine
T enable throwback of errors in the DDE
(Thus, as long as your name doesn't have a `q' or `y' in it, you can
amuse yourself typing your name in as a switch and see what it does.)
Note that these switches can also be selected by putting a `switches'
directive into the source code before anything else, such as
Switches xdv5s;
The most useful switches are `v3' and `v5', which choose between
Standard and Advanced games. For example, the above line is from the
example game `Advent', which is consequently compiled to an Advanced
game. (If no choice is stated, a Standard game results.) The options
`v4' and `v6' are provided for completeness but their use is not
recommended.
Many of the remaining switches make Inform produce extra output, but do
not affect its compilation:
`a b l m n t'
are tracing options to help with maintaining Inform, or for
debugging assembly language programs
`o p s z'
will print out information about the final game file, the `s'
(statistics) option being particularly useful to keep track of how
large the game is growing
`c w T'
in `c' mode, Inform does not quote whole source lines together with
error messages; in `w' mode it suppresses warnings; in `T' mode,
which is only present on the Acorn Archimedes, error throwback
will occur in the `Desktop Development Environment'
`f'
indicates roughly how many bytes the abbreviations saved
`h'
prints out the help information (and is equivalent to just typing
`inform')
`j x'
make Inform print out steady text to prove that it's still awake:
on very slow machines this may be a convenience
`k'
writes a `debugging information' file for the use of the Infix
debugger (similarly, the filename is something suitable for the
machine)
`r'
is intended to help with proof-reading the text of a game, and
transcribes all of the text in double-quotes to the given file
(whose name is something suitable for the machine)
`u'
will try to work out a good set of abbreviations to declare for
your game, but *extremely slowly* (a matter of hours) and
*consuming very much memory* (perhaps a megabyte)
This leaves three more switches which actually alter the game file which
Inform would compile:
`d'
converts text like
"...with a mango. You applaud..."
into the same with only a single space after the full stop, which
will prevent an interpreter from displaying a spurious space at
the beginning of a line when a line break happens to occur exactly
after the full stop; this is to help typists who habitually
double-space
`e'
only in `economy' mode does Inform actually process abbreviations,
because this is seldom needed and slows the compiler by 10% or so;
the game file should not play any differently if compiled this
way, but will probably be shorter, if your choice of abbreviations
was sensible
`g'
will make Inform automatically compile trace-printing code on every
function call; in play this will produce reams of text (several
pages between each chance to type commands) but is sometimes
useful. Note that in Inform 5.3 or later, this can be set on an
individual command by writing `*' as its first local variable,
without use of the `g' switch
`i'
overrides any switches set by `switches' directives in the source
code; so that the game can be compiled with different options
without having to alter that source code.
E.2 Memory settings
===================
Inform's memory management is about as flexible as it can be given that
it has to run in some quite hostile environments. In particular, it is
unable to increase the size of any stretch of memory once allocated, so
if it runs out of anything it has to give up. If it does run out, it
will produce an error message saying what it has run out of and how to
provide more.
There are two main choices: `$small' and `$large'. (Which one is the
default depends on the computer you use.) Even `$small' is large
enough to compile all the example games, including `Advent'. `$large'
is large enough to compile almost anything (including the largest
version of `Curses' which ever existed, a draft very close to 256K
long).
A typical game, compiled with `$large', will cause Inform to allocate
about 336K of memory: and the same game about 100K less under `$small'.
(These values will be rather lower if the computer Inform runs on has
16-bit integers.) In addition, Inform physically occupies about 170K
(on my computer). Thus, the total memory consumption of the compiler
at work will be between 4 to 500K.
Running
inform $list
will list the various settings which can be changed, and their current
values. Thus one can compare small and large with:
inform $small $list
inform $large $list
If Inform runs out of allocation for something, it will generally print
an error message like:
"Game", line 1320: Fatal error: The memory setting MAX_OBJECTS (which
is 200 at present) has been exceeded. Try running Inform again with
$MAX_OBJECTS=<some-larger-number> on the command line.
and indeed
inform $MAX_OBJECTS=250 game
(say) will tell Inform to try again, reserving more memory for objects
this time. Note that settings are made from left to right, so that for
instance
inform $small $MAX_ACTIONS=200 ...
will work, but
inform $MAX_ACTIONS=200 $small ...
will not because the `$small' changes `MAX_ACTIONS' again.
Changing some settings has hardly any effect on memory usage, whereas
others are expensive to increase. To find out about, say, `MAX_VERBS',
run
inform $?MAX_VERBS
(note the question mark, which may need to be escaped using e.g., `\?'
if your shell interprets `?' as a filename wildcard) which will print
some very brief comments.
F Answers to all the exercises
******************************
F.1 Answer: the green cone
==========================
Nearby cone "green cone"
with name "green" "cone" "emerald" "marzipan",
describe [;
if (cone has moved)
"A misshapen cone of green marzipan sits here.";
"Nearby is an emerald green cone, one foot high.";
],
description "The cone seems to be made of emerald-coloured \
marzipan.",
before [;
Eat:
if (random(100) <= 30) {
deadflag = 1;
"Unfortunately, you seem to be allergic to almonds.";
}
"You nibble at a corner of the cone.";
],
after [;
Take: "Taken. (Your hands are smeared with marzipan.)";
Drop:
cone.description = "The cone is a vague green mess.";
"The cone drops to the floor and sags a little.";
],
has edible;
The old `initial' message has gone. Instead, we have provided a
`describe' routine. Whenever the game has to describe the cone in the
description of a place, it will call this routine. The `moved'
attribute is held only by an object which has at some time in the past
been taken. So the cone is now perfect and untouched until taken and
dropped, whereupon it becomes misshapen. Also, the act of dropping the
cone now changes the description which appears when a player examines
it.
F.2 Answer: actions
===================
You may be surprised how many actions take place: often more than one
per turn.
F.3 Answer: property addresses
==============================
`if (obj.&door_to == 0) { ... }'
F.4 Answer: world colours
=========================
Define four objects along the lines of:
Object white_obj "white wall" compass
with name "white" "sac" "wall",
article "the",
door_dir n_to
has direction scenery;
and add the following line to `Initialise':
remove n_obj; remove e_obj; remove w_obj; remove s_obj;
(We could even `alias' a new property `white_to' to be `n_to', and then
enter map directions in the source code using Mayan property names.)
As a fine point of style, turquoise (*yax*) is the world colour for
`here', so add a grammar line to make this cause a `look':
Verb "turquoise" "yax" * -> Look;
F.5 Answer: reflecting the map
==============================
[ SwapDirs o1 o2 x;
x=o1.door_dir;
o1.door_dir=o2.door_dir;
o2.door_dir=x;
];
[ ReflectWorld;
SwapDirs(e_obj,w_obj);
SwapDirs(ne_obj,nw_obj);
SwapDirs(se_obj,sw_obj);
];
F.6 Answer: reflecting directions
=================================
This is a prime candidate for using variable strings `@nn', a topic
properly covered in the `Inform Technical Manual', but briefly: at the
head of the source, define
Lowstring east_str "east";
Lowstring west_str "west";
and then add two more routines to the game,
[ NormalWorld;
String 0 #east_str;
String 1 #west_str;
];
[ ReversedWorld;
String 0 #west_str;
String 1 #east_str;
];
where `NormalWorld' is called in `Initialise' or to go back to normal,
and `ReversedWorld' when the reflection happens. Write `@00' in place
of `east' in any double-quoted printable string, and similarly `@01'
for `west'. It will be printed as whichever is currently set. (Inform
provides up to 32 such variable strings.)
F.7 Answer: medicine bottle
===========================
Declare a fake action called, say, `OpenUp'. Then:
Object medicine "guaranteed child-proof medicine bottle" cupboard
with name "medicine" "bottle",
description "~Antidote only: no preventative effect.~",
before [;
Open, Unlock: "It's adult-proof too.";
Openup:
give self open ~locked;
"The bottle cracks open!";
],
has container openable locked;
Any other code in the game can execute `<OpenUp medicine>' to crack
open the bottle.
F.8 Answer: two boxes
=====================
Nearby glass_box "glass box with a lid"
with name "glass" "box" "with" "lid"
has container transparent openable open;
Nearby steel_box "steel box with a lid"
with name "steel" "box" "with" "lid"
has container openable open;
F.9 Answer: toothed bag
=======================
Object bag "toothed bag" room
with name "toothed" "bag",
description "A capacious bag with a toothed mouth.",
before [;
LetGo: "The bag defiantly bites itself shut on your hand \
until you desist.";
Close: "The bag resists all attempts to close it.";
],
after [;
Receive:
print "The bag wriggles hideously as it swallows ";
DefArt(inp1); ".";
],
has container open;
F.10 Answer: television set
===========================
Object television "portable television set" lounge
with name "tv" "television" "set" "portable",
before [;
SwitchOn: <<SwitchOn power_button>>;
SwitchOff: <<SwitchOff power_button>>;
Examine: <<Examine screen>>;
],
has transparent;
Nearby power_button "power button"
with name "power" "button" "switch",
after [;
SwitchOn, SwitchOff: <<Examine screen>>;
],
has switchable;
Nearby screen "television screen"
with name "screen",
before [;
Examine:
if (power_button hasnt on) "The screen is black.";
"The screen writhes with a strange Japanese cartoon.";
];
F.11 Answer: the red car
========================
Change the car's `before' to
before [;
Go:
if (noun==d_obj or u_obj) {
print "(The car will never get over those stairs.)^";
rfalse;
}
if (car has on) "Brmm! Brmm!";
print "(The ignition is off at the moment.)^";
],
F.12 Answer: *wayhel*
=====================
The common man's *wayhel* was a lowly mouse. Since we think much more
highly of the player:
Object hog "Warthog" Caldera
with name "wart" "hog" "warthog", description "Muddy and grunting.",
number 0,
initial "A warthog snuffles and grunts about in the ash.",
before [;
if (player==self && action~=##Go or ##Look or ##Examine)
"Warthogs can't do anything as tricky as that!";
],
has animate proper;
and we just `ChangePlayer(warthog);'. Note that this `before' rule is
carefully written only to affect actions of the player-as-warthog. If
the player-as-human should find and try to `take warthog', this
`before' routine won't interfere.
F.13 Answer: printing objects
=============================
Because `CDefArt(obj)' is a function call which, as it happens, returns
the value true, or 1 (not that this signifies anything), and `print'
thinks it is printing out a number.
F.14 Answer: the thief
======================
This is a crude implementation, for brevity (the real Zork I thief has
an enormous stock of attached messages).
Object thief "thief" Danger_Zone
with name "thief",
each_turn "^The thief growls menacingly.",
daemon [ i p j n k;
if (random(3)~=1) rfalse;
p=parent(thief);
objectloop (i in compass) {
j=p.(i.door_dir);
if (j>player && j<=top_object && j hasnt door) n++;
}
if (n==0) rfalse;
k=random(n); n=0;
objectloop (i in compass) {
j=p.(i.door_dir);
if (j>player && j<=top_object && j hasnt door) n++;
if (n==k) {
move self to j;
if (p==location) "^The thief stalks away!";
if (j==location) "^The thief stalks in!";
rfalse;
}
}
];
This thief walks at random and cannot pass through doors, bridges and
the like (because these may be locked or have rules attached); it's only
a first approximation, and in a good game one should occasionally see
the thief do something surprising, such as open a secret door.
F.15 Answer: weights
====================
First define a new property for object weight:
Property weight 10;
(10 being an average sort of weight). Containers weigh more when they
hold things, so we will need:
[ WeightOf obj t i;
t = obj.weight;
objectloop (i in obj) t = t + WeightOf(i);
return t;
];
Now for the daemon which monitors the player's fatigue:
Object weigher "weigher"
with number 500,
time_left 5,
daemon [ w s b bw;
w = WeightOf(player) - 100 - player.weight;
s = self.number;
s = s - w;
if (s <0) s = 0;
if (s > 500) s = 500;
self.number = s;
if (s == 0) {
bw = -1;
objectloop(b in player)
if (WeightOf(b) > bw) {
bw = WeightOf(b);
w = b;
}
print "^Exhausted with carrying so much, you decide \
to discard ";
DefArt(w);
print ": ";
<<Drop w>>;
}
w = s/100;
if (w == self.time_left) rfalse;
if (w == 3)
print "^You are feeling a little tired.^";
if (w == 2)
print "^You possessions are weighing you down.^";
if (w == 1)
print "^Carrying so much weight is wearing you out.^";
self.time_left = w;
];
Notice that items are actually dropped with `Drop' actions: one of them
might be, say, a wild boar, which would bolt away into the forest when
released. The daemon tries to drop the heaviest item. (Obviously a
little improvement would be needed if the game contained, say, an
un-droppable but very heavy ball and chain.) Now the daemon is going to
run every turn forever, but needs to be started: so put
`StartDaemon(weigher);' into the game's `Initialise' routine.
F.16 Answer: passing midnight
=============================
Either set a daemon to watch for `the_time' suddenly dropping, or put
such a watch in the game's `TimePasses' routine.
F.17 Answer: suspended in mid-air
=================================
Because you don't know what order daemons will run in. A `fatigue'
daemon which makes the player drop something might come after the
`mid-air' daemon has run for this turn. Whereas `each_turn' happens
after daemons and timers have run their course, and can fairly assume
no further movements will take place this turn.
F.18 Answer: varying rates of time
==================================
It would have to provide its own code to keep track of time, and it can
do this by providing a `TimePasses()' routine. Providing `time' or
even `date' verbs to tell the player would also be a good idea.
F.19 Answer: footnotes
======================
Constant MAX_FOOTNOTES 10;
global footnotes_seen data MAX_FOOTNOTES;
global footnote_count;
[ Note n i pn;
for (i = 0: i < footnote_count: i++)
if (n == footnotes_seen->i)
pn = i;
if (footnote_count == MAX_FOOTNOTES)
"** MAX_FOOTNOTES exceeded! **";
if (pn == 0) {
pn = footnote_count++;
footnotes_seen->pn = n;
}
print " [", pn + 1, "]";
];
[ FootnoteSub n;
if (noun > footnote_count) {
print "No footnote [",noun,"] has been mentioned.^";
rtrue;
}
if (noun == 0) "Footnotes count upward from 1.";
n = footnotes_seen->(noun-1);
print "[", noun, "] ";
if (n==0) "This is a footnote.";
if (n==1) "D.G.REG.F.D is inscribed around English coins.";
if (n==2) "~Jackdaws love my big sphinx of quartz~, for example.";
];
Verb "footnote" "note"
* number -> Footnote;
And then call, for instance, `Note(1);' to refer in the game to the
`English coins' footnote.
F.20 Answer: unknown verbs
==========================
Because the parser might go on to reject the line it's working on: for
instance, if the player typed `inventory splurge' then the message
`Shazam!' followed by a parser complaint will be somewhat unedifying.
F.21 Answer: control panel
==========================
Here goes: we could implement the buttons with five separate objects,
essentially duplicates of each other. (And by using a class definition,
this wouldn't look too bad.) But if there were 500 slides this would be
less reasonable.
[ ASlide w n;
if (location ~= Machine_Room) ! Slides only make sense in
return -1; ! the Machine Room
w = NextWord();
if (w == 'slide') w = NextWord();
n = 0;
if (w == 'first' or 'one') n=1;
if (w == 'second' or 'two') n=2;
if (w == 'third' or 'three') n=3;
if (w == 'fourth' or 'four') n=4;
if (w == 'fifth' or 'five') n=5;
if (n == 0) return -1; ! Failure!
w = NextWord();
if (w ~= 'slide') wn--; ! Move word counter back to
! first misunderstood word
parsed_number=n;
return 1; ! Success!
];
Global slide_settings data 10; ! Ten bytes of data to hold
! five words for the settings
! (all initially zero)
! An interesting point here is that "noun" and "second" contain the
! appropriate numbers, and not objects: this all happens automatically
[ SetSlideSub;
slide_settings-->(noun-1) = second;
print_ret "You set slide number ", noun,
" to the value ", second, ".";
];
[ XSlideSub;
print_ret "Slide number ", noun, " currently stands at ",
slide_settings-->(noun-1), ".";
];
Extend "set" first
* ASlide "to" number -> SetSlide;
Extend "push" first
* ASlide "to" number -> SetSlide;
Extend "examine" first
* ASlide -> XSlide;
F.22 Answer: quoted strings
===========================
The blackboard code in `Toyshop' contains just such a routine:
Global from_char; Global to_char;
[ QuotedText i j f;
i = parse->((++wn) * 4 - 3);
if (buffer->i == '"') {
for (j = i + 1: j <= (buffer->1) + 1: j++)
if (buffer->j == '"') f = j;
if (f == 0) return -1;
from_char = i + 1;
to_char = f - 1;
if (from_char>to_char) return -1;
while (f > (parse->(wn * 4 - 3))) wn++;
wn++;
return 1;
}
return -1;
];
Note that in the case of success, the word marker `wn' is moved beyond
the last word accepted (since the Z-machine automatically tokenises a
double-quote as a single word). The routine then tells the parser it
has parsed a number, which is a white lie. What it actually does is to
record the byte positions where the quoted text starts and finishes in
the raw text `buffer' so that an action routine can easily extract the
text and use it later. (Note that `""' with no text inside is not
matched by this routine but only because the last `if' statement throws
out that one case.)
F.23 Answer: named rooms
========================
Define two properties:
Property place_name;
Property long to_places;
The scheme will work like this: a named room should have the
`place_name' property set to a single dictionary word; say, the
Bedquilt cave could be called `'bedquilt''. Then in any room, a list
of those other rooms which can be moved to in this way should appear in
the `to_places' entry. For instance,
to_places Bedquilt Slab_Room Twopit_Room;
Now the code: see if a not-understood verb is a place name of a nearby
room, and if so store that room's object number in `goto_room',
converting the verb to a dummy.
Global goto_room = 0;
[ UnknownVerb word p i;
p = location.&to_places;
if (p == 0) rfalse;
for (i = 0: (2 * i) < location.#to_places: i++)
if (word == (p-->i).place_name) {
goto_room = p-->i;
return 'go#room';
}
rfalse;
];
[ PrintVerb word;
if (word=='go#room') {
print "go to ";
PrintShortName(goto_room);
rtrue;
}
rfalse;
];
(The supplied `PrintVerb' is icing on the cake: so the parser can say
something like `I only understood you as far as wanting to go to
Bedquilt.' if need be.) It remains only to put in code and grammar for
the dummy verb:
[ GoRoomSub;
if (goto_room hasnt visited)
"But you have never been there.";
PlayerTo(goto_room);
];
Verb "go#room"
* -> GoRoom;
Note that if you don't know the way, you can't go there! A purist might
prefer instead to not recognise the name of an unvisited room, back at
the `UnknownVerb' stage, to avoid the player being able to deduce names
of nearby rooms from this `error message'.
F.24 Answer: purloin verb
=========================
A slight refinement of such a `purloin' verb is already defined in the
library (if the constant `DEBUG' is defined), so there's no need.
Here's how it could be done:
[ Anything i;
if (scope_stage==1) rfalse;
if (scope_stage==2) {
for (i=1:i<=top_object:i++) PlaceInScope(i);
rtrue;
}
"No such in game.";
];
(This disallows multiple matches for efficiency reasons - the parser
has enough work to do with such a huge scope definition as it is.) Now
the token `scope=Anything' will match anything at all, even things like
the abstract concept of `east'.
F.25 Answer: light switch
=========================
For good measure, we'll combine this with the previous rule about
`moved' objects being in scope in the dark. The following can be
inserted into the `Shell' game:
Object coal "dull coal" Blank_Room
with name "dull" "coal";
Object Dark_Room "Dark Room"
with description "An empty room with a west exit.",
each_turn [;
if (self has general) self.each_turn=0;
else "^You hear the breathing of a dwarf.";
],
w_to Blank_Room;
Nearby light_switch "light switch"
with name "light" "switch",
initial "On one wall is the light switch.",
after [;
SwitchOn: give Dark_Room light;
SwitchOff: give Dark_Room ~light;
],
has switchable static;
Nearby diamond "shiny diamond"
with name "shiny" "diamond"
has scored;
Nearby dwarf "dwarf"
with name "voice" "dwarf",
life [;
Order:
if (action == ##SwitchOn && noun == light_switch) {
give Dark_Room light general;
give light_switch on;
"~Right you are, squire.~";
}
],
has animate;
[ InScope person i;
if (parent(person) == Dark_Room) {
if (person == dwarf || Dark_Room has general)
PlaceInScope(light_switch);
}
if (person == player && location == thedark) {
objectloop (i near player)
if (i has moved || i == dwarf)
PlaceInScope(i);
}
rfalse;
];
Note that the routine puts the light switch in scope for the dwarf - if
it didn't, the dwarf would not be able to understand `dwarf, turn light
on', and that was the whole point.
F.26 Answer: heliotropic troll
==============================
Just test if `HasLightSource(gift)==1'.
F.27 Answer: double inventory
=============================
[ DoubleInvSub i count1 count2;
print "You are carrying ";
objectloop (i in player) {
if (i hasnt worn) { give i workflag; count1++; }
else { give i ~workflag; count2++; }
}
if (count1==0) print "nothing.";
else
WriteListFrom(child(player), FULLINV_BIT + ENGLISH_BIT +
RECURSE_BIT + WORKFLAG_BIT);
if (count2==0) ".";
print ". In addition, you are wearing ";
objectloop (i in player) {
if (i hasnt worn) give i ~workflag; else give i workflag;
}
WriteListFrom(child(player), ENGLISH_BIT + RECURSE_BIT +
WORKFLAG_BIT);
".";
];
F.28 Answer: cherubim
=====================
Global c_warned = 0;
Class cherub_class
with parse_name [ i j flag;
for (flag = 1: flag == 1: flag = 0) {
j=NextWord();
if (j=='cherub' or j==self.name) flag=1;
if (j=='cherubs' && c_warned==0) {
c_warned=1;
parser_action=##PluralFound;
flag=1;
print "(I'll let this go once, but the plural of \
cherub is cherubim.)^";
}
if (j=='cherubim') {
parser_action=##PluralFound;
flag=1;
}
i++;
}
return i-1;
];
Then again, Shakespeare even writes `cherubins' in `Twelfth Night', so
who are we to censure?
F.29 Answer: status line
========================
First put the directive `Replace DrawStatusLine;' before including the
library. Then add the following routine anywhere after
`treasures_found', an `Advent' variable, is defined:
[ DrawStatusLine;
@split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
spaces (0->33)-1;
@set_cursor 1 2; PrintShortName(location);
if (treasures_found > 0) {
@set_cursor 1 50; print "Treasure: ", treasures_found;
}
@set_cursor 1 1; style roman; @set_window 0;
];
F.30 Answer: invisiclues
========================
Note the magic line of assembly code here, which only works for Advanced
games:
[ GiveHint hint keypress;
print_paddr hint; new_line; new_line;
@read_char 1 0 0 keypress;
if (keypress == 'H' or 'h') rfalse;
rtrue;
];
And a typical menu item using it:
if (menu_item==1) {
print "(Press ENTER to return to menu, or H for another hint.)^^";
if (GiveHint("(1/3) What kind of bird is it, exactly?") == 1)
return 2;
if (GiveHint("(2/3) Magpies are attracted by shiny items.") == 1)
return 2;
"(3/3) Wave at the magpie with the kitchen foil.";
}
F.31 Answer: primes
===================
`Primes(100)', where:
[ Primes i j k l;
for (j = 2: j <= i: j++) {
print j, " : ";
l=j;
while (l > 1)
for (k=2: k<=l: k++)
if (l % k == 0) {
l=l/k;
print k, " ";
break;
}
new_line;
}
];
(which was the first algorithm ever compiled by Inform).
G Index of concepts
*******************
* Menu:
* `Acheton': Credits.
* `Advent': Memory settings.
* `Advent': The player.
* `Advent': Classes of objects.
* `Advent': Misc grammar.
* `Advent': About daemons.
* `Advent': Status line.
* `Advent': Compiler options.
* `Balances': Plural names.
* `Balances': Extending.
* `Colossal Cave': Misc grammar.
* `Curses': Memory settings.
* `Curses': Object definitions.
* `Enchanter': Doors.
* `Hello Cruel World': Shell.
* `Shell': Shell.
* `Spellbreaker': Library actions.
* `Spellbreaker': The player.
* `The Legend Lives': Numbers.
* `Toyshop': Changing lists.
* `Toyshop': Answer (quoted strings).
* `Toyshop': About daemons.
* `Trinity': Room code.
* `Zork I': Each turn.
* `Zork I': About daemons.
* `Zork': Switches.
* `Zork': Z-machine.
* abbreviations: Compiler options.
* abbreviations: Limitations.
* abstract verb: Debugging verbs.
* action groups: Library actions.
* action numbers: Causing actions.
* actions: Debugging verbs.
* actions: Causing actions.
* actions: Basic ingredients.
* actions: Invoking actions.
* actions verb: Debugging verbs.
* actions verb: Basic ingredients.
* addresses: Z-machine.
* Advanced games: Dirty tricks.
* Advanced games: Z-machine.
* Advanced games: Styles.
* Advanced games: Object definitions.
* Advanced games: Properties.
* Advanced games: Warnings.
* Advanced games: Limitations.
* Advanced games: Compiler options.
* almond poisoning: Adding code.
* Anderson, Timothy: Vehicles.
* appallingly convenient verb: Tokens.
* archaeological dig: Each turn.
* Area 400: Naming of names.
* Aristotle: Objects.
* arithmetic operators: Operators.
* arrays: Variables and arrays.
* asking questions: Changing scope.
* assembler errors: Internal and assembler errors.
* assembly language: Dirty tricks.
* assignments: Assignments.
* attributes: Simple objects.
* attributes: Attributes.
* attributes in library: Library attributes.
* Auden, W. H.: Doors.
* audibility: Each turn.
* background daemon: About daemons.
* backslash: File format.
* backslash: Quoted strings.
* bag of six coins: Plural names.
* Baggett, David M.: Numbers.
* Baggett, David M.: The player.
* banana: Tokens.
* batteries: Switches.
* Bedquilt Room: New actions.
* Beerbohm, Max: Scope.
* binary: Constants.
* blackboard: Answer (quoted strings).
* Blank, Marc: Vehicles.
* Blank, Marc: Doors.
* blocks of code: Blocks of code.
* boldface: Styles.
* bolted cupboard: Other containers.
* Booth, Connie: Special effects.
* brace mismatch: Errors.
* braces: Blocks of code.
* brain transference machine: The player.
* brass lantern: Switches.
* built-in functions: Built-in functions.
* byte address: Z-machine.
* Cambridge University: Credits.
* ceiling: Library objects.
* chair: The player.
* changing scope: Global scope.
* changing the player: The player.
* character graphic: Fixed font.
* cherubim: Answer (cherubim).
* cherubim: Plural names.
* Chesterton, G. K.: Printing of names.
* Christopher, John: Places.
* class errors: Errors.
* classes: Classes of objects.
* clearing the screen: Dirty tricks.
* Cleese, John: Special effects.
* closing credits: Amusing.
* clues: Menus.
* command line syntax: Compiler options.
* comments: File format.
* companion volumes: History.
* compiler switches: Compiler options.
* conditional compilation: Directives.
* conditional compilation: Errors.
* conditions: Conditions.
* constants: Constants.
* control constructs: Control constructs.
* copyright: Copyright.
* crashing the interpreter: Crashes.
* creature token: Tokens.
* crowns: Plural names.
* Crowther, Willie: Credits.
* crystal chandelier: Map.
* cursor keys: Dirty tricks.
* daemon running order: Answer (suspended in mid-air).
* daemons: Debugging verbs.
* daemons: About daemons.
* darkness: Scope rules.
* de la Bruyère, Jean: The player.
* de Montaigne, Michel: Creatures.
* debugging code: Trace.
* debugging information file: Compiler options.
* debugging verbs: Debugging verbs.
* default value of properties: Properties.
* definite article: Printing of names.
* definition of darkness: Light.
* dentist's chair: Vehicles.
* depressed philosophers: The player.
* dictionary errors: Errors.
* dictionary resolution: Limitations.
* dictionary words: Library properties.
* direction objects: Library objects.
* direction objects: Answer (world colours).
* direction objects: Library properties.
* direction objects): Room code.
* direction properties: Library properties.
* directions: Map.
* directions: Scope rules.
* directive: Attributes.
* directives: Directives.
* dirty tricks: Dirty tricks.
* displays beautiful: Boxes.
* division by zero: Assignments.
* Donne, John: Places.
* double inventory: Answer (double inventory).
* double inventory: Lists.
* double spacing: Compiler options.
* double-quote: Answer (quoted strings).
* dramatic effects: Global scope.
* drawings: Fixed font.
* drunk player object: The player.
* dummy verb: Answer (named rooms).
* earshot: Each turn.
* economy mode: Compiler options.
* Eliot, T. S.: Naming of names.
* embedded routines: Object definitions.
* embedded routines: Routines.
* entry points: Library entry points.
* epigrams: Boxes.
* error messages: Error messages.
* exotic forms of death: The player.
* expression errors: Errors.
* expressions: Operators.
* fake actions: Causing actions.
* fake actions: Creatures.
* fake actions: Basic containers.
* fake actions: Plural names.
* fatal errors: Fatal errors.
* fatigue daemon: Answer (weights).
* file format: File format.
* flags: Attributes.
* flexible verbs: Misc grammar.
* floating object: Library attributes.
* floor: Library objects.
* fluorescent jellyfish: Light.
* focus of game: The player.
* footnotes: Numbers.
* footnotes: Answer (footnotes).
* foreign character sets: Quoted strings.
* Frankenstein: The player.
* free verb: Tokens.
* function arguments: Routines.
* function keys: Dirty tricks.
* fuses: Timers.
* Gilbert, W. S.: Inventories.
* glass box: Basic containers.
* global variables: Errors.
* Goldsmith, Oliver: Limitations.
* goto verb: Debugging verbs.
* Grammar: Basic ingredients.
* Grammar: Shell.
* grammar lines: New verbs.
* grammar tokens: Tokens.
* grammatical errors: Errors.
* green cone: Simple objects.
* grues: Changing scope.
* hacker and urchin: Creatures.
* hanging elses: Blocks of code.
* hangover: Object definitions.
* has light: Light.
* hash character: Directives.
* held token: Tokens.
* hexadecimal: Properties.
* Ideas, Ivan O.: Scoring.
* in scope: Scope rules.
* indefinite article: Printing of names.
* indistinguishable: Plural names.
* Infact: Infix.
* Infix: Compiler options.
* Infix: Infix.
* Infocom, Inc.: Credits.
* Inform 5.2: Credits.
* InfoTaskForce: Dirty tricks.
* inheritance: Object definitions.
* inheritance: Classes of objects.
* initial possessions: The player.
* internal errors: Internal and assembler errors.
* interpreter: Z-machine.
* interpreters: Dirty tricks.
* inventories: Changing lists.
* Invisiclues: Answer (invisiclues).
* Invisiclues: Menus.
* junior astronaut: Library entry points.
* keyboard: Dirty tricks.
* Kierkegaard, Søren: The player.
* labels: Labels.
* large memory: Memory settings.
* Lebling, P. David: Containers.
* Lebling, P. David: Vehicles.
* Lebling, P. David: Doors.
* Lewis, C. S.: Library.
* Lewis, C. S.: Daemons.
* lexicon: Basic ingredients.
* library: Ingredients.
* library routines: Library routines.
* light and dark: Light.
* light switch: Answer (light switch).
* light switch: Global scope.
* limitations: Limitations.
* line of sight: Each turn.
* listing objects: Lists.
* little red car: Answer (the red car).
* little red car: Vehicles.
* local variables: Variables and arrays.
* Long Count: Changing scope.
* loop over every object: Control constructs.
* low mist: Causing actions.
* low numbers in French: Numbers.
* MacNeice, Louis: Introduction.
* making actions: New actions.
* making attributes: Attributes.
* making grammar: New actions.
* making grammar: New verbs.
* making properties: Properties.
* map: Doors.
* map: Map.
* master catburglar: Library entry points.
* matchbook: Changing lists.
* Mayan directions: Answer (world colours).
* Mayan directions: Room code.
* me: The player.
* medicine bottle: New actions.
* memory consumption: Memory settings.
* memory management: Memory settings.
* memory map: Z-machine.
* memory settings: Memory settings.
* memory settings: Fatal errors.
* memory size: Limitations.
* menu of text options: Menus.
* mid-air location: Each turn.
* Molière: The parser.
* moving the player: The player.
* multiexcept token: Tokens.
* multiheld token: Tokens.
* myself: The player.
* nagual: Answer (wayhel).
* nagual: The player.
* named rooms: Misc grammar.
* named rooms: Answer (named rooms).
* narrow inventory: Lists.
* Nelson, Graham: Copyright.
* NetHack: Dirty tricks.
* normal rules: Causing actions.
* normal rules: Library attributes.
* notify verb: Score.
* noun token: Tokens.
* number token: Tokens.
* number-parsing: Numbers.
* numbers: Z-machine.
* object definitions: Object definitions.
* object errors: Errors.
* Object syntax: Object definitions.
* objects: Object tree.
* objects in library: Library objects.
* offers light: Light.
* ogre with limited patience: Each turn.
* out verb: Vehicles.
* output streams: Dirty tricks.
* oyster: New actions.
* packed address: Z-machine.
* Parker, Dorothy: Boxes.
* Parser: Shell.
* parser: The parser.
* parser questions: Misc grammar.
* parser speed: Errors.
* parser tracing and levels: Trace.
* parsing quoted strings: Misc grammar.
* parsing quoted strings: Answer (quoted strings).
* passing messages: New actions.
* Pepper Room: Causing actions.
* perfectly sensible: Properties.
* Peter and Jane: Constants.
* pinfocom: Dirty tricks.
* plagiarism: Scoring.
* plaster of paris: Vehicles.
* player's origin: The player.
* plural objects: Plural names.
* Pope, Alexander: Daemons.
* precedence: Classes of objects.
* prime factorisations: Answer (primes).
* prime factorisations: Control constructs.
* printing commands: Printing commands.
* proper noun: Printing of names.
* properties: Simple objects.
* properties: Properties.
* properties in library: Library properties.
* proportional font: Fixed font.
* puff of garlic: Printing commands.
* purloin verb: Debugging verbs.
* purloin verb: Changing scope.
* purloin verb: Answer (purloin verb).
* questions: Changing scope.
* questions (yes or no): Yes or no.
* quoted text: Answer (quoted strings).
* quotes off verb: Boxes.
* radio: Each turn.
* rainbows: Map.
* real time: Dirty tricks.
* Rees, Gareth: History.
* reflecting the map: Answer (reflecting the map).
* reflecting the map): Room code.
* release number: Directives.
* replacing grammar: Extending.
* resurrection: The player.
* returning from routines: Returning from routines.
* reusing attributes: Attributes.
* reverse video: Styles.
* roman text: Styles.
* routine errors: Errors.
* routine tracing: Trace.
* routines: Routines.
* routines verb: Debugging verbs.
* rucksack: Tokens.
* run-time crashes: Crashes.
* run-time format: Limitations.
* running out of memory: Fatal errors.
* rusty door: Doors.
* scope: Scope rules.
* score notification: Score.
* score verb: Misc grammar.
* scoring systems: Score.
* screen tricks: Dirty tricks.
* Seal, David: Credits.
* searchlight: Switches.
* see-through: Scope rules.
* serial number: Directives.
* Shakespeare, William: Scope.
* Shakespeare, William: Answer (cherubim).
* Shaw, George Bernard: Scope.
* shazam verb: Misc grammar.
* Shirley, James: Actions.
* silver bars: Classes of objects.
* Sloping Corridor: Map.
* small array: Properties.
* small memory: Memory settings.
* snavig spell: The player.
* Snow, C. P.: Light.
* sound effect: Dirty tricks.
* source-level debugger: Infix.
* Space Invaders: Dirty tricks.
* spaceship control panel: Misc grammar.
* spaceship control panel: Answer (control panel).
* special effects: Special effects.
* special objects: Library objects.
* special token: Tokens.
* Spellbreaker cubes: Plural names.
* Square Room: Map.
* stack pointer: Variables and arrays.
* Standard games: Z-machine.
* Standard games: Compiler options.
* Standard games: Dirty tricks.
* Standard games: Warnings.
* Standard games: Attributes.
* Standard games: Object definitions.
* Standard games: Limitations.
* Standard games: Properties.
* statistics: Compiler options.
* status line: Status line.
* status line: Crashes.
* steel box: Basic containers.
* steel grate: Doors.
* story files: Inform.
* stream: Each turn.
* style of list: Lists.
* sullen snake: Creatures.
* switches (on command line): Compiler options.
* sword: Each turn.
* symbol names: Errors.
* synonyms: New verbs.
* take verb: New verbs.
* Tartt, Donna: Actions.
* tasty food: Object definitions.
* team of four adventurers: The player.
* teleportation: The player.
* television set: Other containers.
* television set: Answer (television set).
* Tera: Credits.
* Texinfo: History.
* text style: Styles.
* Thackray, Jonathan: Credits.
* The Prisoner: Vehicles.
* thief in `Zork I': About daemons.
* thief in `Zork I': Answer (the thief).
* thief in `Zork I': Each turn.
* tidying-up operations: About daemons.
* time of day: Each turn.
* time sequence: Each turn.
* timed input: Dirty tricks.
* timers: Timers.
* timers: Debugging verbs.
* timers verb: Debugging verbs.
* toffee apple: Causing actions.
* token for `any object': Answer (purloin verb).
* tokenisation: Answer (quoted strings).
* tokens: Tokens.
* toothed bag: Answer (toothed bag).
* toothed bag: Basic containers.
* trace verb: Debugging verbs.
* tracing a routine: Trace.
* tracing routines: Debugging verbs.
* tracing the parser: Trace.
* transcript: Dirty tricks.
* treasure class: Classes of objects.
* tree of objects: Object tree.
* tree verb: Debugging verbs.
* troll: Light.
* two-way door: Doors.
* txd (disassembler): Crashes.
* types (lack of): Properties.
* underlining: Styles.
* undo verb: Limitations.
* undo verb: Dirty tricks.
* urchin and hacker: Creatures.
* valuable cake: Classes of objects.
* variable strings: Answer (reflecting directions).
* variables: Variables and arrays.
* vehicles: Vehicles.
* VerbLib: Shell.
* version 6: Limitations.
* very last resort: Replace.
* vocabulary size: Limitations.
* VT100: Styles.
* walls: Library objects.
* wandering monsters: About daemons.
* warnings: Warnings.
* warthog: Answer (wayhel).
* weights: Answer (weights).
* weights: About daemons.
* weird thing: Naming of names.
* what is a grue: Changing scope.
* wide inventory: Lists.
* Wittgenstein, Ludwig: The parser.
* Wittgenstein, Ludwig: Creatures.
* woodpecker: Debugging.
* Woods, Don: Credits.
* world colours: Answer (world colours).
* world colours: Room code.
* xyzzy verb: New verbs.
* Z-machine: Z-machine.
* zero: Numbers.
* Zip: Dirty tricks.
* Zip: Crashes.
* Zip: Infix.
* zterp: Dirty tricks.
H Index of functions, properties, attributes etc
************************************************
* Menu:
* *: Trace.
* *: Routines.
* ++: Errors.
* -: Errors.
* ->: Variables and arrays.
* ->: Variables and arrays.
* ->: New verbs.
* @@: Quoted strings.
* @nn: Answer (reflecting directions).
* absent: Library attributes.
* Achieved: Library routines.
* Achieved(task): Score.
* actor: Global scope.
* additive: Properties.
* additive: Classes of objects.
* after: Simple objects.
* after: Library properties.
* after: Library actions.
* after: Room code.
* AfterLife: Library entry points.
* AfterLife: The player.
* alias: Errors.
* alias: Properties.
* alias: Attributes.
* AllowPushDir: Library routines.
* AllowPushDir: Vehicles.
* ALWAYS_BIT: Lists.
* Amusing: Library entry points.
* AMUSING_PROVIDED: Amusing.
* AMUSING_PROVIDED: Library constants.
* animate: Library attributes.
* ANIMA_PE: Parser errors.
* Answer: Creatures.
* aread: Dirty tricks.
* article: Printing of names.
* article: Library properties.
* Ask: Creatures.
* ASKSCOPE_PE: Parser errors.
* Attack: Creatures.
* Attribute: Attributes.
* autosearch: Library attributes.
* beep: Dirty tricks.
* before: Library properties.
* before: Adding code.
* Blorple: New actions.
* box: Boxes.
* break: Control constructs.
* buffer_mode: Dirty tricks.
* CANTSEE_PE: Parser errors.
* cant_go: Map.
* cant_go: Library properties.
* capacity: Basic containers.
* capacity: Library properties.
* capacity: The player.
* CDefArt: Library routines.
* CDefArt(obj): Printing of names.
* ChangePlayer: Library routines.
* ChangePlayer: The player.
* child: Crashes.
* child: Object tree.
* children: Crashes.
* class: Object definitions.
* Class: Object definitions.
* class: Classes of objects.
* clothing: Library attributes.
* compass: Library objects.
* concealed: Scope rules.
* concealed: Library attributes.
* container: Basic containers.
* container: Library attributes.
* daemon: About daemons.
* daemon: Library properties.
* DarkToDark: Library entry points.
* DarkToDark: Light.
* data: Variables and arrays.
* deadflag: The player.
* deadflag: Adding code.
* DeathMessage: The player.
* DeathMessage: Library entry points.
* DEBUG: Library constants.
* DEBUG: Basic ingredients.
* DEBUG: Debugging verbs.
* DefArt: Library routines.
* DefArt(obj): Printing of names.
* DEFART_BIT: Lists.
* describe: Library properties.
* describe: Object definitions.
* description: Library properties.
* direction: Library attributes.
* DoMenu: Library routines.
* DoMenu: Menus.
* door: Doors.
* door: Library attributes.
* door_dir: Doors.
* door_dir: Doors.
* door_dir: Library properties.
* door_to: Library properties.
* door_to: Doors.
* d_obj: Library objects.
* each_turn: Library properties.
* each_turn: Each turn.
* edible: Library attributes.
* elder: Object tree.
* eldest: Object tree.
* EnglishNumber: Library routines.
* ENGLISH_BIT: Lists.
* Enter: Doors.
* enterable: Library attributes.
* enterable: Vehicles.
* erase_window: Dirty tricks.
* et_flag: Each turn.
* EXCEPT_PE: Parser errors.
* Extend: Extending.
* e_obj: Library objects.
* Fake Action: New actions.
* female: Library attributes.
* first: Extending.
* font: Fixed font.
* for: Control constructs.
* found_in: Library properties.
* found_in: Properties.
* found_in: Causing actions.
* FULLINV_BIT: Lists.
* GamePostRoutine: Library entry points.
* GamePostRoutine: New actions.
* GamePreRoutine: Library entry points.
* GamePreRoutine: New actions.
* general: Library attributes.
* Give: Creatures.
* give: Attributes.
* Global: Variables and arrays.
* Go: Room code.
* Go: Vehicles.
* has: Object definitions.
* has: Attributes.
* HasLightSource: Library routines.
* HasLightSource(object): Light.
* Headline: Library constants.
* if: Blocks of code.
* InDefArt: Library routines.
* InDefArt(obj): Printing of names.
* INDENT_BIT: Lists.
* indirect: Crashes.
* initial: Library properties.
* initial: Simple objects.
* initial: Library properties.
* initial: Variables and arrays.
* Initialise: The player.
* Initialise: Shell.
* Initialise: Library entry points.
* Initialise: Each turn.
* Initialise: Library entry points.
* initstr: Variables and arrays.
* initstr: Variables and arrays.
* InScope: Library entry points.
* Insert: Basic containers.
* invent: Changing lists.
* invent: Library properties.
* inventory_stage: Changing lists.
* in_obj: Library objects.
* ITGONE_PE: Parser errors.
* jump: Labels.
* JUNKAFTER_PE: Parser errors.
* Kiss: Creatures.
* last: Extending.
* LetGo: Basic containers.
* life: Creatures.
* life: Library properties.
* light: Library attributes.
* light: Attributes.
* location: The player.
* lockable: Library attributes.
* locked: Library attributes.
* locked: Basic containers.
* long: Properties.
* LookRoutine: Library entry points.
* Lowstring: Answer (reflecting directions).
* MAX_CARRIED: Library constants.
* MAX_CARRIED: Max carried.
* MAX_SCORE: Library constants.
* MAX_SCORE: Score.
* MAX_SCORES: Score.
* MAX_TIMERS: Library constants.
* MAX_TIMERS: Timers.
* meta: Library actions.
* meta: Misc grammar.
* MMULTI_PE: Parser errors.
* move: Object tree.
* moved: Library attributes.
* moved: Global scope.
* MULTI_PE: Parser errors.
* name: Object definitions.
* name: Naming of names.
* name: Object definitions.
* name: Library properties.
* Nearby: Object definitions.
* NEWLINE_BIT: Lists.
* NewRoom: Library entry points.
* NextWord: Numbers.
* NextWord: Library routines.
* ne_obj: Library objects.
* NOTHELD_PE: Parser errors.
* nothing: Object tree.
* nothing: Crashes.
* NOTHING_PE: Parser errors.
* notify_mode: Score.
* number: Library properties.
* NUMBER_PE: Parser errors.
* NUMBER_TASKS: Library constants.
* NUMBER_TASKS: Score.
* NUMBER_TASKS: Score.
* nw_obj: Library objects.
* n_obj: Library objects.
* Object: Object definitions.
* objectloop: Control constructs.
* OBJECT_SCORE: Library constants.
* OBJECT_SCORE: Score.
* OBJECT_SCORE: Score.
* OffersLight: Library routines.
* OffersLight(object): Light.
* on: Switches.
* on: Library attributes.
* open: Library attributes.
* open: Basic containers.
* openable: Basic containers.
* openable: Library attributes.
* or: Conditions.
* Order: Creatures.
* out_obj: Library objects.
* parent: Crashes.
* parent: Object tree.
* parsed_number: Numbers.
* ParseNumber: Library entry points.
* ParseNumber: Numbers.
* ParserError: Library entry points.
* ParserError: Parser errors.
* parser_action: Plural names.
* parser_one: Plural names.
* parser_two: Plural names.
* parse_name: Plural names.
* parse_name: Naming of names.
* parse_name: Library properties.
* PARTINV_BIT: Lists.
* PlaceInScope: Changing scope.
* PlaceInScope: Library routines.
* PlayerTo: The player.
* PlayerTo: Crashes.
* PlayerTo: Library routines.
* plural: Plural names.
* plural: Library properties.
* print: Room code.
* print object: Printing of names.
* print object: Crashes.
* PrintRank: Library entry points.
* PrintRank: Score.
* PrintShortName: Library routines.
* PrintShortName(obj): Printing of names.
* PrintTaskName: Library entry points.
* PrintVerb: Misc grammar.
* PrintVerb: Library entry points.
* print_addr: Crashes.
* print_paddr: Crashes.
* proper: Printing of names.
* proper: Library attributes.
* Property: Properties.
* PushDir: Vehicles.
* random: Crashes.
* read_char: Dirty tricks.
* Receive: Basic containers.
* RECURSE_BIT: Lists.
* Remove: Basic containers.
* Replace: Replace.
* replace: Extending.
* ROOM_SCORE: Score.
* ROOM_SCORE: Score.
* ROOM_SCORE: Library constants.
* SACK_OBJECT: Library constants.
* SACK_OBJECT: Max carried.
* scenery: Library attributes.
* scenery: Map.
* SCENERY_PE: Parser errors.
* ScopeWithin: Library routines.
* ScopeWithin: Library routines.
* scope_stage: Changing scope.
* scored: Library attributes.
* Search: Library actions.
* self: Causing actions.
* selfobj: Library objects.
* selfobj: Control constructs.
* SetTime: Each turn.
* SetTime: Library routines.
* set_cursor: Dirty tricks.
* set_window: Dirty tricks.
* se_obj: Library objects.
* short_name: Naming of names.
* short_name: Library properties.
* Show: Creatures.
* sibling: Object tree.
* sp: Variables and arrays.
* special_number: Creatures.
* special_word: Creatures.
* split_window: Dirty tricks.
* StartDaemon: Library routines.
* StartDaemon: About daemons.
* StartTimer: Library routines.
* StartTimer: Timers.
* static: Library attributes.
* static: Doors.
* Statusline: Each turn.
* StopDaemon: About daemons.
* StopDaemon: Library routines.
* StopTimer: Timers.
* StopTimer: Library routines.
* Story: Library constants.
* string: Variables and arrays.
* String: Answer (reflecting directions).
* STUCK_PE: Parser errors.
* style: Styles.
* supporter: Library attributes.
* supporter: Basic containers.
* supporter: Vehicles.
* switchable: Library attributes.
* switchable: Switches.
* sw_obj: Library objects.
* sw__var: Object definitions.
* s_obj: Library objects.
* talkable: Library attributes.
* TASKS_PROVIDED: Score.
* TASKS_PROVIDED: Library constants.
* task_scores: Score.
* TERSE_BIT: Lists.
* thedark: The player.
* thedark: Library objects.
* TheSame: Plural names.
* the_time: Each turn.
* ThrowAt: Creatures.
* ThrowAt: Creatures.
* TimePasses: Library entry points.
* time_left: Library properties.
* time_out: Library properties.
* time_out: Timers.
* TOOFEW_PE: Parser errors.
* TOOLIT_PE: Parser errors.
* top_object: Control constructs.
* transparent: Other containers.
* transparent: Creatures.
* transparent: Library attributes.
* transparent: Basic containers.
* TryNumber: Library routines.
* UnknownVerb: Misc grammar.
* UnknownVerb: Library entry points.
* UPTO_PE: Parser errors.
* u_obj: Library objects.
* VAGUE_PE: Parser errors.
* Verb: New verbs.
* VERB_PE: Parser errors.
* visited: Library attributes.
* when_closed: Doors.
* when_closed: Library properties.
* when_off: Library properties.
* when_on: Library properties.
* when_open: Library properties.
* when_open: Doors.
* with: Object definitions.
* with_key: Library properties.
* with_key: Lockable containers.
* wn: Numbers.
* workflag: Library attributes.
* WORKFLAG_BIT: Lists.
* worn: Library attributes.
* WriteListFrom: Library routines.
* WriteListFrom: Lists.
* w_obj: Library objects.
* YesOrNo: Library routines.
* YesOrNo: Yes or no.
* younger: Object tree.
* youngest: Object tree.